Header check: defer unrestricted dependency walks Precompute only the dependency reachability needed to permit includes. Leave the unrestricted walk, which also follows private dependencies transitively, to the existing lazy lookup when no permitted path is found. This avoids building large diagnostic tables for every checked target. Use a shared lock for reachability cache hits, taking the exclusive lock only on a miss. Chromium `gn gen --check`, medians of five round-robin rounds: Mac (M4 Max): 3.13 s -> 2.70 s GCE c4d-384: 5.15 s -> 4.22 s Intel 13900KS: 3.30 s -> 2.55 s CPU time (c4d-standard-384): User: 38.5 s -> 31.6 s (-18%) Sys: 50.2 s -> 33.9 s (-32%) Heap stats: Allocs (count): 54.24 M -> 53.78 M (-1%) Heap (total): 24.92 GB -> 19.78 GB (-21%) Heap (peak): 5.46 GB -> 3.22 GB (-41%) Change-Id: Idbd45609b60d465f78b240db93a33695af34c131 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/26342 Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Philipp Wollermann <philwo@google.com> Reviewed-by: Matt Stark <msta@google.com>
diff --git a/src/gn/header_checker.cc b/src/gn/header_checker.cc index 84b4433..c54a875 100644 --- a/src/gn/header_checker.cc +++ b/src/gn/header_checker.cc
@@ -239,15 +239,15 @@ targets_to_precompute.push_back(info.target); } } - DoWorkChunked(pool, targets_to_precompute, 32, - [this](std::span<const Target* const> chunk) { - for (const Target* target : chunk) { - ReachabilityCache& cache = - GetReachabilityCacheForTarget(target); - cache.PerformDependencyWalk(true); - cache.PerformDependencyWalk(false); - } - }); + // Only the permitted walk decides whether an include is allowed. The + // unrestricted walk over private dependencies is computed on demand by + // SearchForDependencyTo to describe a rejected include. + DoWorkChunked( + pool, targets_to_precompute, 32, + [this](std::span<const Target* const> chunk) { + for (const Target* target : chunk) + GetReachabilityCacheForTarget(target).PerformDependencyWalk(true); + }); } RunCheckOverFiles(files, pool); @@ -771,7 +771,13 @@ const Target* target) const { size_t shard_index = target->label().hash() % kNumShards; auto& shard = dependency_cache_[shard_index]; - std::unique_lock<std::shared_mutex> lock(shard.lock); + { + std::shared_lock<std::shared_mutex> read_lock(shard.lock); + auto it = shard.cache.find(target); + if (it != shard.cache.end()) + return *it->second; + } + std::unique_lock<std::shared_mutex> write_lock(shard.lock); auto it = shard.cache.find(target); if (it == shard.cache.end()) { it =