From 945a1080afc7f79d50e166428af754a3e5d5d91c Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Sat, 24 May 2014 21:17:35 -0700 Subject: Add a stat cache. Demo-quality, and disabled atm. --- src/disk_interface.cc | 119 ++++++++++++++++++++++++++++++++++++++++++-------- src/disk_interface.h | 9 +++- src/ninja.cc | 6 +++ 3 files changed, 114 insertions(+), 20 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 4dfae1a..2175051 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -55,6 +55,29 @@ int MakeDir(const string& path) { #endif } +TimeStamp StatSingleFile(const string& path, bool quiet) { + WIN32_FILE_ATTRIBUTE_DATA attrs; + if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) { + DWORD err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) + return 0; + if (!quiet) { + Error("GetFileAttributesEx(%s): %s", path.c_str(), + GetLastErrorString().c_str()); + } + return -1; + } + const FILETIME& filetime = attrs.ftLastWriteTime; + // FILETIME is in 100-nanosecond increments since the Windows epoch. + // We don't much care about epoch correctness but we do want the + // resulting value to fit in an integer. + uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | + ((uint64_t)filetime.dwLowDateTime); + mtime /= 1000000000LL / 100; // 100ns -> s. + mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years). + return (TimeStamp)mtime; +} + } // namespace // DiskInterface --------------------------------------------------------------- @@ -89,26 +112,84 @@ TimeStamp RealDiskInterface::Stat(const string& path) { } return -1; } - WIN32_FILE_ATTRIBUTE_DATA attrs; - if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) { - DWORD err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND || err == ERROR_PATH_NOT_FOUND) - return 0; - if (!quiet_) { - Error("GetFileAttributesEx(%s): %s", path.c_str(), - GetLastErrorString().c_str()); - } - return -1; + if (!use_cache_) + return StatSingleFile(path, quiet_); + + string dir = DirName(path); + int offs = dir.size(); + if (offs) ++offs; // skip \ too + string base(path.substr(offs)); + + std::transform(dir.begin(), dir.end(), dir.begin(), ::tolower); + std::transform(base.begin(), base.end(), base.begin(), ::tolower); + + Cache::iterator ci = cache_.find(dir); + if (ci != cache_.end()) { + DirCache::iterator di = ci->second->find(base); + if (di != ci->second->end()) + return di->second; + return 0; } - const FILETIME& filetime = attrs.ftLastWriteTime; - // FILETIME is in 100-nanosecond increments since the Windows epoch. - // We don't much care about epoch correctness but we do want the - // resulting value to fit in an integer. - uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | - ((uint64_t)filetime.dwLowDateTime); - mtime /= 1000000000LL / 100; // 100ns -> s. - mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years). - return (TimeStamp)mtime; + + if (dir.empty()) + dir = "."; + // XXX fill in dir using FFF / FNF + HANDLE hFind = INVALID_HANDLE_VALUE; + WIN32_FIND_DATAA ffd; + +#if 0 + hFind = FindFirstFileA((dir + "\\*").c_str(), &ffd); +#else + hFind = FindFirstFileExA((dir + "\\*").c_str(), + //FindExInfoStandard, + FindExInfoBasic, // 30% faster than FindExInfoStandard! + &ffd, + FindExSearchNameMatch, + NULL, + 0 ); // XXX: check FIND_FIRST_EX_LARGE_FETCH +#endif + + if (hFind == INVALID_HANDLE_VALUE) { + fprintf(stderr, "fail %s", dir.c_str()); + exit(-1); + } + DirCache* dc = new DirCache; + do { + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + continue; + + string lowername = ffd.cFileName; + std::transform(lowername.begin(), lowername.end(), + lowername.begin(), ::tolower); + +//fprintf(stderr, "%s %s\n", dir.c_str(), lowername.c_str()); + + const FILETIME& filetime = ffd.ftLastWriteTime; + // FILETIME is in 100-nanosecond increments since the Windows epoch. + // We don't much care about epoch correctness but we do want the + // resulting value to fit in an integer. + uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | + ((uint64_t)filetime.dwLowDateTime); + mtime /= 1000000000LL / 100; // 100ns -> s. +//if(mtime == 0) +//printf(" asdasdfadsfsadf\n"); + + mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years). + + (*dc).insert(make_pair(lowername, (TimeStamp)mtime)); + } while (FindNextFileA(hFind, &ffd)); + FindClose(hFind); + + if (dir == ".") + cache_.insert(make_pair("", dc)); + else + cache_.insert(make_pair(dir, dc)); + + DirCache::iterator di = dc->find(base); + if (di != dc->end()) + return di->second; + return 0; + #else struct stat st; if (stat(path.c_str(), &st) < 0) { diff --git a/src/disk_interface.h b/src/disk_interface.h index ff1e21c..d368c38 100644 --- a/src/disk_interface.h +++ b/src/disk_interface.h @@ -15,6 +15,7 @@ #ifndef NINJA_DISK_INTERFACE_H_ #define NINJA_DISK_INTERFACE_H_ +#include #include using namespace std; @@ -55,7 +56,7 @@ struct DiskInterface { /// Implementation of DiskInterface that actually hits the disk. struct RealDiskInterface : public DiskInterface { - RealDiskInterface() : quiet_(false) {} + RealDiskInterface() : quiet_(false), use_cache_(false) {} virtual ~RealDiskInterface() {} virtual TimeStamp Stat(const string& path); virtual bool MakeDir(const string& path); @@ -65,6 +66,12 @@ struct RealDiskInterface : public DiskInterface { /// Whether to print on errors. Used to make a test quieter. bool quiet_; + /// Whether stat information can be cached. + bool use_cache_; + + typedef map DirCache; + typedef map Cache; + Cache cache_; }; #endif // NINJA_DISK_INTERFACE_H_ diff --git a/src/ninja.cc b/src/ninja.cc index 4c8dab7..962480e 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -882,6 +882,9 @@ int NinjaMain::RunBuild(int argc, char** argv) { return 1; } +// XXX allow stat caching + //disk_interface_.use_cache_ = true; + Builder builder(&state_, config_, &build_log_, &deps_log_, &disk_interface_); for (size_t i = 0; i < targets.size(); ++i) { if (!builder.AddTarget(targets[i], &err)) { @@ -895,6 +898,9 @@ int NinjaMain::RunBuild(int argc, char** argv) { } } +// XXX disallow stat caching + disk_interface_.use_cache_ = false; + if (builder.AlreadyUpToDate()) { printf("ninja: no work to do.\n"); return 0; -- cgit v0.12