[clang] [clang-tools-extra] [lldb] [llvm] Reland "[Support][JSON] Use std::unordered_map for object storage (#171230)" (PR #193628)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 22 16:46:18 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-support

Author: Zixu Wang (zixu-w)

<details>
<summary>Changes</summary>

#<!-- -->171230 was reverted because of Buildbot failures with libstdc++.

libstdc++ eagerly instantiates `std::pair<const ObjectKey, Value>` with `std::unordered_map<ObjectKey, Value>`, requiring both types to be complete. This creates a circular dependency: `json::Object` needs `json::Value` complete, and `json::Value` needs `json::Object` complete (for `AlignedCharArrayUnion`).

The fix stores a `std::unique_ptr<json::Object>` in Value's union instead of inline, breaking the cycle. This allows reordering the type definitions as `ObjectKey -> Array -> Value -> Object`, so both key and value types are complete when the `unordered_map` is instantiated.

This also changes how `json::Value` is laid out in memory. With #<!-- -->171230, `sizeof(json::Object)` grows from the change to `std::unordered_map`, so `sizeof(json::Value::Union)` also increases. In this fix, the size of `Union` shrinks because we use `std::unique_ptr`, with the trade-off of an additional allocation outside of `AlignedCharArrayUnion`'s buffer to create `json::Object` (only occurring for object-typed values).

Benchmarked again and the performance is roughly the same as in #<!-- -->171230:
```
Running benchmarks/JSONParserBM
Run on (20 X 24 MHz CPU s)
CPU Caches:
  L1 Data 64 KiB
  L1 Instruction 128 KiB
  L2 Unified 4096 KiB (x20)
Load Average: 5.58, 13.78, 10.19
-----------------------------------------------------------------------------------------
Benchmark                               Time             CPU   Iterations UserCounters...
-----------------------------------------------------------------------------------------
BM_JSONParse/10                     21614 ns        21608 ns        31732 AllocBytes=26.048k Allocs=640 ParseByteRate=142.822Mi/s
BM_JSONParse/1000                 2307481 ns      2306885 ns          304 AllocBytes=2.55368M Allocs=63.016k ParseByteRate=137.234Mi/s
BM_JSONParse/100000             237053194 ns    236974667 ns            3 AllocBytes=257.189M Allocs=6.30002M ParseByteRate=137.611Mi/s
BM_JSONIterate/10                     373 ns          372 ns      1881650 items_per_second=515.516M/s
BM_JSONIterate/1000                 36334 ns        36330 ns        19185 items_per_second=523.046M/s
BM_JSONIterate/100000             7852453 ns      7851031 ns          159 items_per_second=242.007M/s
BM_JSONLookupSequential/10           1695 ns         1694 ns       414088 items_per_second=89.1237M/s
BM_JSONLookupSequential/1000       167926 ns       167893 ns         4156 items_per_second=89.3484M/s
BM_JSONLookupSequential/100000   17563538 ns     17554429 ns           35 items_per_second=85.4486M/s
BM_JSONLookupRandom/10               1777 ns         1776 ns       395003 items_per_second=85.0015M/s
BM_JSONLookupRandom/1000           189113 ns       189087 ns         3712 items_per_second=79.334M/s
BM_JSONLookupRandom/100000       22329233 ns     22325500 ns           36 items_per_second=67.1878M/s
```

Assisted-by: Claude Code (claude-opus-4-6)

---

Patch is 32.64 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/193628.diff


12 Files Affected:

- (modified) clang-tools-extra/clang-doc/JSONGenerator.cpp (+1-1) 
- (modified) clang/include/clang/Basic/Sarif.h (+1) 
- (modified) clang/lib/Basic/DarwinSDKInfo.cpp (+7-7) 
- (modified) clang/tools/clang-installapi/Options.cpp (+2-2) 
- (modified) clang/unittests/Basic/SarifTest.cpp (+1-1) 
- (modified) lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h (+2) 
- (modified) lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h (+1) 
- (modified) llvm/benchmarks/CMakeLists.txt (+1) 
- (added) llvm/benchmarks/JSONParserBM.cpp (+299) 
- (modified) llvm/include/llvm/Support/JSON.h (+140-121) 
- (modified) llvm/lib/Support/JSON.cpp (+7-5) 
- (modified) llvm/tools/llvm-mca/Views/InstructionInfoView.h (+1) 


``````````diff
diff --git a/clang-tools-extra/clang-doc/JSONGenerator.cpp b/clang-tools-extra/clang-doc/JSONGenerator.cpp
index e895d641a6000..d4d12083197d5 100644
--- a/clang-tools-extra/clang-doc/JSONGenerator.cpp
+++ b/clang-tools-extra/clang-doc/JSONGenerator.cpp
@@ -155,7 +155,7 @@ static void insertComment(Object &Description, json::Value &Comment,
     Description[Key] = std::move(CommentsArray);
     Description["Has" + Key.str()] = true;
   } else {
-    DescriptionIt->getSecond().getAsArray()->push_back(Comment);
+    DescriptionIt->second.getAsArray()->push_back(Comment);
   }
 }
 
diff --git a/clang/include/clang/Basic/Sarif.h b/clang/include/clang/Basic/Sarif.h
index 7651d2ac7a768..a9099271ccc0c 100644
--- a/clang/include/clang/Basic/Sarif.h
+++ b/clang/include/clang/Basic/Sarif.h
@@ -34,6 +34,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/Version.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/ADT/StringRef.h"
diff --git a/clang/lib/Basic/DarwinSDKInfo.cpp b/clang/lib/Basic/DarwinSDKInfo.cpp
index f7d02ef97f5a4..39d08387f8d1a 100644
--- a/clang/lib/Basic/DarwinSDKInfo.cpp
+++ b/clang/lib/Basic/DarwinSDKInfo.cpp
@@ -25,7 +25,7 @@ std::optional<VersionTuple> DarwinSDKInfo::RelatedTargetVersionMapping::map(
     return MaximumValue;
   auto KV = Mapping.find(Key.normalize());
   if (KV != Mapping.end())
-    return KV->getSecond();
+    return KV->second;
   // If no exact entry found, try just the major key version. Only do so when
   // a minor version number is present, to avoid recursing indefinitely into
   // the major-only check.
@@ -43,10 +43,10 @@ DarwinSDKInfo::RelatedTargetVersionMapping::parseJSON(
   VersionTuple MinValue = Min;
   llvm::DenseMap<VersionTuple, VersionTuple> Mapping;
   for (const auto &KV : Obj) {
-    if (auto Val = KV.getSecond().getAsString()) {
+    if (auto Val = KV.second.getAsString()) {
       llvm::VersionTuple KeyVersion;
       llvm::VersionTuple ValueVersion;
-      if (KeyVersion.tryParse(KV.getFirst()) || ValueVersion.tryParse(*Val))
+      if (KeyVersion.tryParse(KV.first) || ValueVersion.tryParse(*Val))
         return std::nullopt;
       Mapping[KeyVersion.normalize()] = ValueVersion;
       if (KeyVersion < Min)
@@ -119,7 +119,7 @@ static DarwinSDKInfo::PlatformInfoStorageType parsePlatformInfos(
 
   for (auto SupportedTargetPair : *SupportedTargets) {
     llvm::json::Object *SupportedTarget =
-        SupportedTargetPair.getSecond().getAsObject();
+        SupportedTargetPair.second.getAsObject();
     auto Vendor = SupportedTarget->getString("LLVMTargetTripleVendor");
     auto OS = SupportedTarget->getString("LLVMTargetTripleSys");
     if (!Vendor || !OS)
@@ -136,7 +136,7 @@ static DarwinSDKInfo::PlatformInfoStorageType parsePlatformInfos(
 
     // The key is either the Xcode platform, or a variant. The platform must be
     // the first entry in the returned PlatformInfoStorageType.
-    StringRef PlatformOrVariant = SupportedTargetPair.getFirst();
+    StringRef PlatformOrVariant = SupportedTargetPair.first;
 
     StringRef EffectivePlatformPrefix;
     // Ignore iosmac value if it exists.
@@ -202,12 +202,12 @@ DarwinSDKInfo::parseDarwinSDKSettingsJSON(std::string FilePath,
     // FIXME: Generalize this out beyond iOS-deriving targets.
     // Look for ios_<targetos> version mapping for targets that derive from ios.
     for (const auto &KV : *VM) {
-      auto Pair = StringRef(KV.getFirst()).split("_");
+      auto Pair = StringRef(KV.first).split("_");
       if (Pair.first.compare_insensitive("ios") == 0) {
         llvm::Triple TT(llvm::Twine("--") + Pair.second.lower());
         if (TT.getOS() != llvm::Triple::UnknownOS) {
           auto Mapping = RelatedTargetVersionMapping::parseJSON(
-              *KV.getSecond().getAsObject(), *MaximumDeploymentVersion);
+              *KV.second.getAsObject(), *MaximumDeploymentVersion);
           if (Mapping)
             VersionMappings[OSEnvPair(llvm::Triple::IOS,
                                       llvm::Triple::UnknownEnvironment,
diff --git a/clang/tools/clang-installapi/Options.cpp b/clang/tools/clang-installapi/Options.cpp
index f484d6f33ad8f..1151f65af4dce 100644
--- a/clang/tools/clang-installapi/Options.cpp
+++ b/clang/tools/clang-installapi/Options.cpp
@@ -84,8 +84,8 @@ getArgListFromJSON(const StringRef Input, llvm::opt::OptTable *Table,
     return llvm::opt::InputArgList();
 
   for (const auto &KV : *Root) {
-    const Array *ArgList = KV.getSecond().getAsArray();
-    std::string Label = "-X" + KV.getFirst().str();
+    const Array *ArgList = KV.second.getAsArray();
+    std::string Label = "-X" + KV.first.str();
     if (!ArgList)
       return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat);
     for (auto Arg : *ArgList) {
diff --git a/clang/unittests/Basic/SarifTest.cpp b/clang/unittests/Basic/SarifTest.cpp
index 42e85085d646e..cfae48a96c2a8 100644
--- a/clang/unittests/Basic/SarifTest.cpp
+++ b/clang/unittests/Basic/SarifTest.cpp
@@ -83,7 +83,7 @@ TEST_F(SarifDocumentWriterTest, canCreateEmptyDocument) {
   const llvm::json::Object &EmptyDoc = Writer.createDocument();
   std::vector<StringRef> Keys(EmptyDoc.size());
   std::transform(EmptyDoc.begin(), EmptyDoc.end(), Keys.begin(),
-                 [](auto Item) { return Item.getFirst(); });
+                 [](auto Item) { return Item.first; });
 
   // THEN:
   ASSERT_THAT(Keys, testing::UnorderedElementsAre("$schema", "version"));
diff --git a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
index eebb5117b30c5..603badf3330a7 100644
--- a/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
+++ b/lldb/source/Plugins/DynamicLoader/Windows-DYLD/DynamicLoaderWindowsDYLD.h
@@ -12,6 +12,8 @@
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/lldb-forward.h"
 
+#include "llvm/ADT/DenseMap.h"
+
 namespace lldb_private {
 
 class DynamicLoaderWindowsDYLD : public DynamicLoader {
diff --git a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
index 550cd46127ed9..31388d4b9be82 100644
--- a/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
+++ b/lldb/source/Plugins/Process/Linux/IntelPTThreadTraceCollection.h
@@ -10,6 +10,7 @@
 #define liblldb_IntelPTPerThreadTraceCollection_H_
 
 #include "IntelPTSingleBufferTrace.h"
+#include "llvm/ADT/DenseMap.h"
 #include <optional>
 
 namespace lldb_private {
diff --git a/llvm/benchmarks/CMakeLists.txt b/llvm/benchmarks/CMakeLists.txt
index 69ebeaa78344b..bdd1ce40d2cb2 100644
--- a/llvm/benchmarks/CMakeLists.txt
+++ b/llvm/benchmarks/CMakeLists.txt
@@ -15,6 +15,7 @@ add_benchmark(MustacheBench Mustache.cpp PARTIAL_SOURCES_INTENDED)
 add_benchmark(SpecialCaseListBM SpecialCaseListBM.cpp PARTIAL_SOURCES_INTENDED)
 add_benchmark(DWARFVerifierBM DWARFVerifierBM.cpp PARTIAL_SOURCES_INTENDED)
 add_benchmark(PointerUnionBM PointerUnionBM.cpp PARTIAL_SOURCES_INTENDED)
+add_benchmark(JSONParserBM JSONParserBM.cpp PARTIAL_SOURCES_INTENDED)
 
 add_benchmark(RuntimeLibcallsBench RuntimeLibcalls.cpp PARTIAL_SOURCES_INTENDED)
 
diff --git a/llvm/benchmarks/JSONParserBM.cpp b/llvm/benchmarks/JSONParserBM.cpp
new file mode 100644
index 0000000000000..b5deb0bbf2f66
--- /dev/null
+++ b/llvm/benchmarks/JSONParserBM.cpp
@@ -0,0 +1,299 @@
+//===- JSONParserBM.cpp - JSON parser benchmarks --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// \file
+// Benchmarks for LLVM's JSON parser.
+// Runs parsing, tree iteration, and object key lookup with generated inputs.
+// Measures time performance and memory consumption.
+//
+//===----------------------------------------------------------------------===//
+
+#include "benchmark/benchmark.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/JSON.h"
+#include <algorithm>
+#include <atomic>
+#include <random>
+
+using namespace llvm;
+
+//===----------------------------------------------------------------------===//
+// Memory tracking via global operator new
+//
+// These are global overrides so the benchmark might be over-counting memory
+// allocations and usage. The data is still useful for comparisons with this
+// benchmark itself.
+//===----------------------------------------------------------------------===//
+
+static std::atomic_size_t TotalAllocatedBytes{0};
+static std::atomic_size_t NumAllocs{0};
+static bool TrackMemory = false;
+
+// Single-object new/delete.
+void *operator new(std::size_t Size) {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void *operator new(std::size_t Size, std::align_val_t) {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void *operator new(std::size_t Size, const std::nothrow_t &) noexcept {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void *operator new(std::size_t Size, std::align_val_t,
+                   const std::nothrow_t &) noexcept {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void operator delete(void *Ptr) noexcept { std::free(Ptr); }
+void operator delete(void *Ptr, std::align_val_t) noexcept { std::free(Ptr); }
+void operator delete(void *Ptr, std::size_t) noexcept { std::free(Ptr); }
+void operator delete(void *Ptr, std::size_t, std::align_val_t) noexcept {
+  std::free(Ptr);
+}
+
+// Array new/delete.
+void *operator new[](std::size_t Size) {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void *operator new[](std::size_t Size, std::align_val_t) {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void *operator new[](std::size_t Size, const std::nothrow_t &) noexcept {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void *operator new[](std::size_t Size, std::align_val_t,
+                     const std::nothrow_t &) noexcept {
+  if (TrackMemory) {
+    TotalAllocatedBytes += Size;
+    ++NumAllocs;
+  }
+  return std::malloc(Size);
+}
+
+void operator delete[](void *Ptr) noexcept { std::free(Ptr); }
+void operator delete[](void *Ptr, std::align_val_t) noexcept { std::free(Ptr); }
+void operator delete[](void *Ptr, std::size_t) noexcept { std::free(Ptr); }
+void operator delete[](void *Ptr, std::size_t, std::align_val_t) noexcept {
+  std::free(Ptr);
+}
+
+//===----------------------------------------------------------------------===//
+// Test data generation
+//===----------------------------------------------------------------------===//
+
+/// Generate a JSON string with \p N entries in an array. Each entry is a nested
+/// structure with objects and arrays to exercise parsing, iteration, and lookup
+/// at multiple depths.
+///
+/// Structure:
+///   {"items": [
+///     {
+///       "name": "item_I",
+///       "value": I,
+///       "tags": [
+///         {"label": "tag_0", "priority": 0},
+///         ...
+///       ],
+///       "details": {
+///         "description": "description text for item I",
+///         "active": true/false,
+///         "nested": { "x": I, "y": I*100 }
+///       }
+///     },
+///     ...
+///   ]}
+static std::string generateJSON(int N) {
+  std::string S;
+  raw_string_ostream OS(S);
+  OS << "{\"items\": [\n";
+  for (int I = 0; I < N; ++I) {
+    if (I > 0)
+      OS << ",\n";
+    OS << "  {\n"
+       << "    \"name\": \"item_" << I << "\",\n"
+       << "    \"value\": " << I << ",\n"
+       << "    \"tags\": [\n"
+       << "      {\"label\": \"tag_0\", \"priority\": 0},\n"
+       << "      {\"label\": \"tag_1\", \"priority\": 1},\n"
+       << "      {\"label\": \"tag_2\", \"priority\": 2}\n"
+       << "    ],\n"
+       << "    \"details\": {\n"
+       << "      \"description\": \"description text for item " << I << "\",\n"
+       << "      \"active\": " << (I % 2 == 0 ? "true" : "false") << ",\n"
+       << "      \"nested\": {\"x\": " << I << ", \"y\": " << I * 100 << "}\n"
+       << "    }\n"
+       << "  }";
+  }
+  OS << "\n]}";
+  return S;
+}
+
+//===----------------------------------------------------------------------===//
+// Tree traversal helpers
+//===----------------------------------------------------------------------===//
+
+/// Walk the JSON value tree, visiting every node. Returns the number of
+/// nodes visited.
+static size_t walkTree(const json::Value &V) {
+  size_t Count = 1;
+  if (const auto *Obj = V.getAsObject()) {
+    for (const auto &KV : *Obj)
+      Count += walkTree(KV.second);
+  } else if (const auto *Arr = V.getAsArray()) {
+    for (const auto &Elem : *Arr)
+      Count += walkTree(Elem);
+  }
+  return Count;
+}
+
+/// An Object paired with its own keys, for lookup benchmarks.
+struct ObjectWithKeys {
+  const json::Object *Obj;
+  SmallVector<std::string> Keys;
+};
+
+/// Collect every Object in the tree together with its own keys.
+static void collectObjectsWithKeys(const json::Value &V,
+                                   SmallVectorImpl<ObjectWithKeys> &Result) {
+  if (const auto *Obj = V.getAsObject()) {
+    ObjectWithKeys Entry;
+    Entry.Obj = Obj;
+    for (const auto &KV : *Obj) {
+      Entry.Keys.push_back(std::string(StringRef(KV.first)));
+      collectObjectsWithKeys(KV.second, Result);
+    }
+    Result.push_back(std::move(Entry));
+  } else if (const auto *Arr = V.getAsArray()) {
+    for (const auto &Elem : *Arr)
+      collectObjectsWithKeys(Elem, Result);
+  }
+}
+
+//===----------------------------------------------------------------------===//
+// Benchmarks
+//===----------------------------------------------------------------------===//
+
+/// Benchmark json::parse(). Reports parse throughput and memory allocated.
+static void BM_JSONParse(benchmark::State &State) {
+  std::string JSON = generateJSON(State.range(0));
+
+  // Measure memory for a single parse before the timed loop.
+  TotalAllocatedBytes = 0;
+  NumAllocs = 0;
+  TrackMemory = true;
+  {
+    auto V = json::parse(JSON);
+    benchmark::DoNotOptimize(V);
+  }
+  TrackMemory = false;
+
+  State.counters["AllocBytes"] = TotalAllocatedBytes.load();
+  State.counters["Allocs"] = NumAllocs.load();
+
+  for (auto _ : State) {
+    auto V = json::parse(JSON);
+    benchmark::DoNotOptimize(V);
+  }
+  State.counters["ParseByteRate"] = benchmark::Counter(
+      State.iterations() * JSON.size(), benchmark::Counter::kIsRate,
+      benchmark::Counter::kIs1024);
+}
+BENCHMARK(BM_JSONParse)->Arg(10)->Arg(1000)->Arg(100000);
+
+/// Benchmark recursive tree iteration over a parsed JSON value.
+static void BM_JSONIterate(benchmark::State &State) {
+  std::string JSON = generateJSON(State.range(0));
+  json::Value Root = cantFail(json::parse(JSON));
+  size_t NodeCount = 0;
+  for (auto _ : State) {
+    NodeCount = walkTree(Root);
+    benchmark::DoNotOptimize(NodeCount);
+  }
+  State.SetItemsProcessed(State.iterations() * NodeCount);
+}
+BENCHMARK(BM_JSONIterate)->Arg(10)->Arg(1000)->Arg(100000);
+
+/// Benchmark Object::get() with each object's own keys in insertion order.
+static void BM_JSONLookupSequential(benchmark::State &State) {
+  std::string JSON = generateJSON(State.range(0));
+  json::Value Root = cantFail(json::parse(JSON));
+  SmallVector<ObjectWithKeys> ObjKeys;
+  collectObjectsWithKeys(Root, ObjKeys);
+
+  size_t TotalLookups = 0;
+  for (const auto &OK : ObjKeys)
+    TotalLookups += OK.Keys.size();
+
+  for (auto _ : State) {
+    for (const auto &OK : ObjKeys)
+      for (const auto &K : OK.Keys)
+        benchmark::DoNotOptimize(OK.Obj->get(K));
+  }
+  State.SetItemsProcessed(State.iterations() * TotalLookups);
+}
+BENCHMARK(BM_JSONLookupSequential)->Arg(10)->Arg(1000)->Arg(100000);
+
+/// Benchmark Object::get() with each object's own keys in random order.
+static void BM_JSONLookupRandom(benchmark::State &State) {
+  std::string JSON = generateJSON(State.range(0));
+  json::Value Root = cantFail(json::parse(JSON));
+  SmallVector<ObjectWithKeys> ObjKeys;
+  collectObjectsWithKeys(Root, ObjKeys);
+
+  std::mt19937 RNG(42);
+  size_t TotalLookups = 0;
+  for (auto &OK : ObjKeys) {
+    TotalLookups += OK.Keys.size();
+    std::shuffle(OK.Keys.begin(), OK.Keys.end(), RNG);
+  }
+
+  for (auto _ : State) {
+    for (const auto &OK : ObjKeys)
+      for (const auto &K : OK.Keys)
+        benchmark::DoNotOptimize(OK.Obj->get(K));
+  }
+  State.SetItemsProcessed(State.iterations() * TotalLookups);
+}
+BENCHMARK(BM_JSONLookupRandom)->Arg(10)->Arg(1000)->Arg(100000);
+
+BENCHMARK_MAIN();
diff --git a/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h
index 27862eb7ab6bb..bd429f0106885 100644
--- a/llvm/include/llvm/Support/JSON.h
+++ b/llvm/include/llvm/Support/JSON.h
@@ -46,16 +46,18 @@
 #ifndef LLVM_SUPPORT_JSON_H
 #define LLVM_SUPPORT_JSON_H
 
-#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Hashing.h"
 #include "llvm/ADT/STLFunctionalExtras.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/AlignOf.h"
 #include "llvm/Support/Compiler.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cmath>
 #include <map>
+#include <unordered_map>
 
 namespace llvm {
 namespace json {
@@ -75,10 +77,12 @@ namespace json {
 //   - When retrieving strings from Values (e.g. asString()), the result will
 //     always be valid UTF-8.
 
+template <typename T, bool = std::is_integral_v<T>>
+constexpr bool is_uint_64_bit_v = false;
+
 template <typename T>
-constexpr bool is_uint_64_bit_v =
-    std::is_integral_v<T> && std::is_unsigned_v<T> &&
-    sizeof(T) == sizeof(uint64_t);
+constexpr bool is_uint_64_bit_v<T, true> =
+    std::is_unsigned_v<T> && sizeof(T) == sizeof(uint64_t);
 
 /// Returns true if \p S is valid UTF-8, which is required for use as JSON.
 /// If it returns false, \p Offset is set to a byte offset near the first error.
@@ -89,76 +93,65 @@ LLVM_ABI bool isUTF8(llvm::StringRef S, size_t *ErrOffset = nullptr);
 LLVM_ABI std::string fixUTF8(llvm::StringRef S);
 
 class Array;
-class ObjectKey;
 class Value;
+class Object;
 template <typename T> Value toJSON(const std::optional<T> &Opt);
 
-/// An Object is a JSON object, which maps strings to heterogenous JSON values.
-/// It simulates DenseMap<ObjectKey, Value>. ObjectKey is a maybe-owned string.
-class Object {
-  using Storage = DenseMap<ObjectKey, Value, llvm::DenseMapInfo<StringRef>>;
-  Storage M;
-
+/// ObjectKey is a used to capture keys in Object. Like Value but:
+///   - only strings are allowed
+///   - it's optimized for the string literal case (Owned == nullptr)
+/// Like Value, strings must be UTF-8. See isUTF8 documentation for details.
+class ObjectKey {
 public:
-  using key_type = ObjectKey;
-  using mapped_type = Value;
-  using value_type = Storage::value_type;
-  using iterator = Storage::iterator;
-  using const_iterator = Storage::const_iterator;
-
-  Object() = default;
-  // KV is a trivial key-value struct for list-initialization.
-  // (using std::pair forces extra copies).
-  struct KV;
-  explicit Object(std::initializer_list<KV> Properties);
-
-  iterator begin() { return M.begin(); }
-  const_iterator begin() const { return M.begin(); }
-  iterator end() { return M.end(); }
-  const_iterator end() const { return M.end(); }
-
-  bool empty() const { return M.empty(); }
-  size_t size() const { return M.size(); }
-
-  void clear() { M.clear(); }
-  std::pair<iterator, bool> insert(KV E);
-  template <typename... Ts>
-  std::pair<iterator, bool> try_emplace(const ObjectKey &K, Ts &&... Args) {
-    return M.try_emplace(K, std::forward<Ts>(Args)...);
+  ObjectKey(const char *S) : ObjectKey(StringRef(S)) {}
+  ObjectKey(std::string S) : Owned(new std::string(std::move...
[truncated]

``````````

</details>


https://github.com/llvm/llvm-project/pull/193628


More information about the llvm-commits mailing list