gn gen: Implement thread-local ResolvedTargetData cache.

Final benchmarks for 'gn gen' from the previous stack of CLs
shows significant time reduction for moderate peak RAM increase:

              TIME_BEFORE     TIME_AFTER    RAM_BEFORE  RAM_AFTER

  Chromium:         4.75s          3.30s       1.00GiB    1.31GiB
  Fuchsia:         20.99s         17.98s       3.84GiB    4.07GiB

Benchmark Details ------------------------------------------

Chromium:

  $ hyperfine '/tmp/gn-main gen out/Default' '/tmp/gn-new gen out/Default'
  Benchmark 1: /tmp/gn-main gen out/Default
    Time (mean ± σ):      4.753 s ±  0.044 s    [User: 15.413 s, System: 8.977 s]
    Range (min … max):    4.672 s …  4.820 s    10 runs

  Benchmark 2: /tmp/gn-new gen out/Default
    Time (mean ± σ):      3.303 s ±  0.061 s    [User: 18.202 s, System: 9.122 s]
    Range (min … max):    3.217 s …  3.413 s    10 runs

  Summary
    '/tmp/gn-new gen out/Default' ran
      1.44 ± 0.03 times faster than '/tmp/gn-main gen out/Default'

  $ /usr/bin/time -f%M /tmp/gn-main gen out/Default
  Done. Made 17871 targets from 3048 files in 4674ms
  1049960

  $ /usr/bin/time -f%M /tmp/gn-new gen out/Default
  Done. Made 17871 targets from 3048 files in 3229ms
  1374344

Fuchsia:

  $ hyperfine '/tmp/gn-main gen out/default' '/tmp/gn-new gen out/default'
  Benchmark 1: /tmp/gn-main gen out/default
    Time (mean ± σ):     20.994 s ±  0.277 s    [User: 97.113 s, System: 49.242 s]
    Range (min … max):   20.637 s … 21.602 s    10 runs

  Benchmark 2: /tmp/gn-new gen out/default
    Time (mean ± σ):     17.981 s ±  0.259 s    [User: 95.037 s, System: 48.343 s]
    Range (min … max):   17.390 s … 18.324 s    10 runs

  Summary
    '/tmp/gn-new gen out/default' ran
      1.17 ± 0.02 times faster than '/tmp/gn-main gen out/default'

  $ /usr/bin/time -f%M /tmp/gn-main gen out/default
  Generating compile_commands took 727ms
  Done. Made 196612 targets from 6247 files in 20741ms
  4026004

  $ /usr/bin/time -f%M /tmp/gn-new gen out/default
  Generating compile_commands took 659ms
  Done. Made 196612 targets from 6247 files in 17427ms
  4272248

Bug: 331
Change-Id: Ib2eca769a5a587a531f4fac2be263555e37b89ea
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/15329
Commit-Queue: David Turner <digit@google.com>
Reviewed-by: Takuto Ikuta <tikuta@google.com>
diff --git a/src/gn/command_gen.cc b/src/gn/command_gen.cc
index 6216269..ec4a766 100644
--- a/src/gn/command_gen.cc
+++ b/src/gn/command_gen.cc
@@ -3,6 +3,8 @@
 // found in the LICENSE file.
 
 #include <mutex>
+#include <thread>
+#include <unordered_map>
 
 #include "base/command_line.h"
 #include "base/strings/string_number_conversions.h"
@@ -72,11 +74,23 @@
 struct TargetWriteInfo {
   std::mutex lock;
   NinjaWriter::PerToolchainRules rules;
+
+  using ResolvedMap = std::unordered_map<std::thread::id, ResolvedTargetData>;
+  std::unique_ptr<ResolvedMap> resolved_map = std::make_unique<ResolvedMap>();
+
+  void LeakOnPurpose() {
+    (void)resolved_map.release();
+  }
 };
 
 // Called on worker thread to write the ninja file.
 void BackgroundDoWrite(TargetWriteInfo* write_info, const Target* target) {
-  std::string rule = NinjaTargetWriter::RunAndWriteFile(target);
+  ResolvedTargetData* resolved;
+  {
+    std::lock_guard<std::mutex> lock(write_info->lock);
+    resolved = &((*write_info->resolved_map)[std::this_thread::get_id()]);
+  }
+  std::string rule = NinjaTargetWriter::RunAndWriteFile(target, resolved);
   DCHECK(!rule.empty());
 
   {
@@ -787,6 +801,10 @@
     OutputString(stats);
   }
 
+  // Just like the build graph, leak the resolved data to avoid expensive
+  // process teardown here too.
+  write_info.LeakOnPurpose();
+
   return 0;
 }