[gn] Use std::unordered_map for waiting_map_ Previously, BuilderRecord::waiting_map_ was a base::flat_map. For heavily depended-upon targets (such as core libraries like //base:base and popular configs) that have thousands of dependents, inserting a new dependent in waiting_map_ caused a binary search followed by an O(N) memmove of all subsequent elements to keep the flat_map sorted. Once StringAtomSet global lock contention is eliminated, worker threads parse and submit targets to the Builder at a much higher rate, making these O(N) memmove insertions a major bottleneck on the main thread. However, waiting_map_ only requires point lookup by pointer and full traversals to notify dependents of state changes; it never requires sorted order (and functions like GetSortedUnresolvedDeps already sort their output explicitly). Switching to std::unordered_map eliminates the array shifts on insertion. Benchmark results (`gn gen out/Default` on Chromium, 5 runs with hyperfine, official release build configuration with Fuchsia Clang + libc++ + static libjemalloc.a + LTO): - Stacked on StringAtomSet sharding (CL 26540): 2.258s ± 0.035s -> 2.060s ± 0.064s (-198ms / -8.8%, min 2.011s) - Compared to origin/main: 2.755s ± 0.029s -> 2.060s ± 0.064s (-695ms / 1.34x faster) Output ninja files remain bit-for-bit identical across all 14,639 files. TAG=agy CONV=4cb5d931-60a8-4f44-8632-3e8d9fb10a8d Change-Id: I3174ee798a5f6d0c58736fc05ad4ed2684b1d37c Reviewed-on: https://gn-review.googlesource.com/c/gn/+/26560 Reviewed-by: Matt Stark <msta@google.com> Commit-Queue: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/builder_record.h b/src/gn/builder_record.h index 52d4816..b2bef55 100644 --- a/src/gn/builder_record.h +++ b/src/gn/builder_record.h
@@ -6,6 +6,7 @@ #define TOOLS_GN_BUILDER_RECORD_H_ #include <memory> +#include <unordered_map> #include <utility> #include "base/containers/flat_map.h" @@ -327,7 +328,25 @@ bool wait_validation_defined = false; bool wait_validation_resolved = false; }; - base::flat_map<BuilderRecord*, WaitInfo> waiting_map_; + + // Transparent hash and equality functors to allow heterogeneous lookup with + // `const BuilderRecord*` (e.g. `this` in GetSortedUnresolvedDeps()) while + // keeping mutable `BuilderRecord*` as the map key so dependents can be + // modified during notifications. + struct PtrHash { + using is_transparent = void; + size_t operator()(const BuilderRecord* p) const { + return std::hash<const BuilderRecord*>()(p); + } + }; + struct PtrEqual { + using is_transparent = void; + bool operator()(const BuilderRecord* a, const BuilderRecord* b) const { + return a == b; + } + }; + + std::unordered_map<BuilderRecord*, WaitInfo, PtrHash, PtrEqual> waiting_map_; // Called by NotifyDependentsOfStateChange() when a dependency // of the current record has changed its state. Must return