mirror of
https://github.com/MaskRay/ccls.git
synced 2025-02-22 16:39:48 +00:00
Cache timestamps when importing, which improves import time by 5x on windows.
cquery import goes from 2.5s to 0.5s
This commit is contained in:
parent
274c98f032
commit
49aca3038b
@ -763,6 +763,35 @@ struct CacheLoader {
|
|||||||
Config* config_;
|
Config* config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Caches timestamps of cc files so we can avoid a filesystem reads. This is
|
||||||
|
// important for import perf, as during dependency checking the same files are
|
||||||
|
// checked over and over again if they are common headers.
|
||||||
|
struct TimestampManager {
|
||||||
|
optional<int64_t> GetLastCachedModificationTime(CacheLoader* cache_loader, const std::string& path) {
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
auto it = timestamps_.find(path);
|
||||||
|
if (it != timestamps_.end())
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
IndexFile* file = cache_loader->TryLoad(path);
|
||||||
|
if (!file)
|
||||||
|
return nullopt;
|
||||||
|
|
||||||
|
UpdateCachedModificationTime(path, file->last_modification_time);
|
||||||
|
return file->last_modification_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateCachedModificationTime(const std::string& path, int64_t timestamp) {
|
||||||
|
std::lock_guard<std::mutex> guard(mutex_);
|
||||||
|
timestamps_[path] = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::mutex mutex_; // TODO: use std::shared_mutex so we can have multiple readers.
|
||||||
|
std::unordered_map<std::string, int64_t> timestamps_;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
struct IndexManager {
|
struct IndexManager {
|
||||||
std::unordered_set<std::string> files_being_indexed_;
|
std::unordered_set<std::string> files_being_indexed_;
|
||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
@ -793,6 +822,7 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
Config* config,
|
Config* config,
|
||||||
clang::Index* index,
|
clang::Index* index,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
|
TimestampManager* timestamp_manager,
|
||||||
CacheLoader* cache_loader,
|
CacheLoader* cache_loader,
|
||||||
const std::string& path,
|
const std::string& path,
|
||||||
const std::vector<std::string>& args) {
|
const std::vector<std::string>& args) {
|
||||||
@ -803,15 +833,9 @@ std::vector<Index_DoIdMap> DoParseFile(
|
|||||||
// If none of the dependencies have changed, skip parsing and just load from cache.
|
// If none of the dependencies have changed, skip parsing and just load from cache.
|
||||||
auto file_needs_parse = [&](const std::string& path) {
|
auto file_needs_parse = [&](const std::string& path) {
|
||||||
int64_t modification_timestamp = GetLastModificationTime(path);
|
int64_t modification_timestamp = GetLastModificationTime(path);
|
||||||
// PERF: We don't need to fully deserialize the file from cache here; we
|
optional<int64_t> last_cached_modification = timestamp_manager->GetLastCachedModificationTime(cache_loader, path);
|
||||||
// need to load it into memory but after that we can just check the
|
|
||||||
// timestamp. We may want to actually introduce a third file, so we have
|
if (!last_cached_modification || modification_timestamp != *last_cached_modification) {
|
||||||
// foo.cc, foo.cc.index.json, and foo.cc.meta.json and store the
|
|
||||||
// timestamp in foo.cc.meta.json. We may also want to begin writing
|
|
||||||
// to separate folders so things are not all in one folder (ie, take the
|
|
||||||
// first 5 chars as the folder name).
|
|
||||||
IndexFile* index = cache_loader->TryLoad(path);
|
|
||||||
if (!index || modification_timestamp != index->last_modification_time) {
|
|
||||||
file_consumer_shared->Reset(path);
|
file_consumer_shared->Reset(path);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -915,6 +939,7 @@ std::vector<Index_DoIdMap> ParseFile(
|
|||||||
Config* config,
|
Config* config,
|
||||||
clang::Index* index,
|
clang::Index* index,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
|
TimestampManager* timestamp_manager,
|
||||||
const Project::Entry& entry) {
|
const Project::Entry& entry) {
|
||||||
|
|
||||||
CacheLoader cache_loader(config);
|
CacheLoader cache_loader(config);
|
||||||
@ -924,13 +949,14 @@ std::vector<Index_DoIdMap> ParseFile(
|
|||||||
// complain about if indexed by itself.
|
// complain about if indexed by itself.
|
||||||
IndexFile* entry_cache = cache_loader.TryLoad(entry.filename);
|
IndexFile* entry_cache = cache_loader.TryLoad(entry.filename);
|
||||||
std::string tu_path = entry_cache ? entry_cache->import_file : entry.filename;
|
std::string tu_path = entry_cache ? entry_cache->import_file : entry.filename;
|
||||||
return DoParseFile(config, index, file_consumer_shared, &cache_loader, tu_path, entry.args);
|
return DoParseFile(config, index, file_consumer_shared, timestamp_manager, &cache_loader, tu_path, entry.args);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IndexMain_DoParse(
|
bool IndexMain_DoParse(
|
||||||
Config* config,
|
Config* config,
|
||||||
QueueManager* queue,
|
QueueManager* queue,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
|
TimestampManager* timestamp_manager,
|
||||||
clang::Index* index) {
|
clang::Index* index) {
|
||||||
|
|
||||||
optional<Index_Request> request = queue->index_request.TryDequeue();
|
optional<Index_Request> request = queue->index_request.TryDequeue();
|
||||||
@ -940,7 +966,7 @@ bool IndexMain_DoParse(
|
|||||||
Project::Entry entry;
|
Project::Entry entry;
|
||||||
entry.filename = request->path;
|
entry.filename = request->path;
|
||||||
entry.args = request->args;
|
entry.args = request->args;
|
||||||
std::vector<Index_DoIdMap> responses = ParseFile(config, index, file_consumer_shared, entry);
|
std::vector<Index_DoIdMap> responses = ParseFile(config, index, file_consumer_shared, timestamp_manager, entry);
|
||||||
|
|
||||||
// TODO/FIXME: bulk enqueue so we don't lock so many times
|
// TODO/FIXME: bulk enqueue so we don't lock so many times
|
||||||
for (Index_DoIdMap& response : responses)
|
for (Index_DoIdMap& response : responses)
|
||||||
@ -951,7 +977,8 @@ bool IndexMain_DoParse(
|
|||||||
|
|
||||||
bool IndexMain_DoCreateIndexUpdate(
|
bool IndexMain_DoCreateIndexUpdate(
|
||||||
Config* config,
|
Config* config,
|
||||||
QueueManager* queue) {
|
QueueManager* queue,
|
||||||
|
TimestampManager* timestamp_manager) {
|
||||||
// TODO: Index_OnIdMapped dtor is failing because it seems that its contents have already been destroyed.
|
// TODO: Index_OnIdMapped dtor is failing because it seems that its contents have already been destroyed.
|
||||||
optional<Index_OnIdMapped> response = queue->on_id_mapped.TryDequeue();
|
optional<Index_OnIdMapped> response = queue->on_id_mapped.TryDequeue();
|
||||||
if (!response)
|
if (!response)
|
||||||
@ -978,6 +1005,8 @@ bool IndexMain_DoCreateIndexUpdate(
|
|||||||
time.Reset();
|
time.Reset();
|
||||||
WriteToCache(config, *response->current->file);
|
WriteToCache(config, *response->current->file);
|
||||||
response->perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
|
response->perf.index_save_to_disk = time.ElapsedMicrosecondsAndReset();
|
||||||
|
|
||||||
|
timestamp_manager->UpdateCachedModificationTime(response->current->file->path, response->current->file->last_modification_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if false
|
#if false
|
||||||
@ -1045,6 +1074,7 @@ bool IndexMergeIndexUpdates(QueueManager* queue) {
|
|||||||
|
|
||||||
void IndexMain(Config* config,
|
void IndexMain(Config* config,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
|
TimestampManager* timestamp_manager,
|
||||||
Project* project,
|
Project* project,
|
||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
MultiQueueWaiter* waiter,
|
MultiQueueWaiter* waiter,
|
||||||
@ -1064,10 +1094,10 @@ void IndexMain(Config* config,
|
|||||||
// IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any
|
// IndexMain_DoCreateIndexUpdate so we don't starve querydb from doing any
|
||||||
// work. Running both also lets the user query the partially constructed
|
// work. Running both also lets the user query the partially constructed
|
||||||
// index.
|
// index.
|
||||||
bool did_parse = IndexMain_DoParse(config, queue, file_consumer_shared, &index);
|
bool did_parse = IndexMain_DoParse(config, queue, file_consumer_shared, timestamp_manager, &index);
|
||||||
|
|
||||||
bool did_create_update =
|
bool did_create_update =
|
||||||
IndexMain_DoCreateIndexUpdate(config, queue);
|
IndexMain_DoCreateIndexUpdate(config, queue, timestamp_manager);
|
||||||
|
|
||||||
bool did_load_previous = IndexMain_LoadPreviousIndex(config, queue);
|
bool did_load_previous = IndexMain_LoadPreviousIndex(config, queue);
|
||||||
|
|
||||||
@ -1150,8 +1180,9 @@ bool QueryDb_ImportMain(Config* config, QueryDatabase* db, QueueManager* queue,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
time.Reset();
|
||||||
db->ApplyIndexUpdate(&response->update);
|
db->ApplyIndexUpdate(&response->update);
|
||||||
//time.ResetAndPrint("[querydb] Applying index update");
|
time.ResetAndPrint("Applying index update");
|
||||||
}
|
}
|
||||||
|
|
||||||
return did_work;
|
return did_work;
|
||||||
@ -1239,6 +1270,7 @@ bool QueryDbMainLoop(
|
|||||||
QueueManager* queue,
|
QueueManager* queue,
|
||||||
Project* project,
|
Project* project,
|
||||||
FileConsumer::SharedState* file_consumer_shared,
|
FileConsumer::SharedState* file_consumer_shared,
|
||||||
|
TimestampManager* timestamp_manager,
|
||||||
WorkingFiles* working_files,
|
WorkingFiles* working_files,
|
||||||
ClangCompleteManager* clang_complete,
|
ClangCompleteManager* clang_complete,
|
||||||
IncludeComplete* include_complete,
|
IncludeComplete* include_complete,
|
||||||
@ -1310,7 +1342,7 @@ bool QueryDbMainLoop(
|
|||||||
std::cerr << "[querydb] Starting " << config->indexerCount << " indexers" << std::endl;
|
std::cerr << "[querydb] Starting " << config->indexerCount << " indexers" << std::endl;
|
||||||
for (int i = 0; i < config->indexerCount; ++i) {
|
for (int i = 0; i < config->indexerCount; ++i) {
|
||||||
new std::thread([&]() {
|
new std::thread([&]() {
|
||||||
IndexMain(config, file_consumer_shared, project, working_files, waiter, queue);
|
IndexMain(config, file_consumer_shared, timestamp_manager, project, working_files, waiter, queue);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2532,6 +2564,7 @@ void QueryDbMain(const std::string& bin_name, Config* config, MultiQueueWaiter*
|
|||||||
auto non_global_code_complete_cache = MakeUnique<CodeCompleteCache>();
|
auto non_global_code_complete_cache = MakeUnique<CodeCompleteCache>();
|
||||||
auto signature_cache = MakeUnique<CodeCompleteCache>();
|
auto signature_cache = MakeUnique<CodeCompleteCache>();
|
||||||
FileConsumer::SharedState file_consumer_shared;
|
FileConsumer::SharedState file_consumer_shared;
|
||||||
|
TimestampManager timestamp_manager;
|
||||||
|
|
||||||
// Run query db main loop.
|
// Run query db main loop.
|
||||||
SetCurrentThreadName("querydb");
|
SetCurrentThreadName("querydb");
|
||||||
@ -2539,7 +2572,7 @@ void QueryDbMain(const std::string& bin_name, Config* config, MultiQueueWaiter*
|
|||||||
while (true) {
|
while (true) {
|
||||||
bool did_work = QueryDbMainLoop(
|
bool did_work = QueryDbMainLoop(
|
||||||
config, &db, waiter, &queue,
|
config, &db, waiter, &queue,
|
||||||
&project, &file_consumer_shared, &working_files,
|
&project, &file_consumer_shared, ×tamp_manager, &working_files,
|
||||||
&clang_complete, &include_complete, global_code_complete_cache.get(), non_global_code_complete_cache.get(), signature_cache.get());
|
&clang_complete, &include_complete, global_code_complete_cache.get(), non_global_code_complete_cache.get(), signature_cache.get());
|
||||||
if (!did_work) {
|
if (!did_work) {
|
||||||
waiter->Wait({
|
waiter->Wait({
|
||||||
|
Loading…
Reference in New Issue
Block a user