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

Zixu Wang via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 13 18:48:14 PDT 2026


https://github.com/zixu-w updated https://github.com/llvm/llvm-project/pull/171230

>From 94b70f40903baeea9b0d0d3e570e73f0b17d7274 Mon Sep 17 00:00:00 2001
From: Zixu Wang <zixu_wang at apple.com>
Date: Mon, 8 Dec 2025 15:21:23 -0800
Subject: [PATCH 1/3] [Support][JSON] Use `std::unordered_map` for object
 storage

`llvm::DenseMap` is not suitable for the key-value `Storage` inside
`llvm::json::Object`. Use `std::unordered_map` instead to optimize
memory usage.

`llvm::DenseMap` is optimized for mapping small keys and values
(pointers), and it pre-allocates 64 buckets by default.
`llvm::json::ObjectKey` is 24 bytes in size, and `llvm::json::Value` is
40 bytes. Currently, the JSON parser allocates 4KB of memory for each
JSON object. In practice, most JSON objects contain only a handful of
fields, and likely to have lists of many small objects. This is a
significant waste of memory.
---
 clang-tools-extra/clang-doc/JSONGenerator.cpp |  2 +-
 clang/lib/Basic/DarwinSDKInfo.cpp             | 14 +++++++-------
 clang/tools/clang-installapi/Options.cpp      |  4 ++--
 clang/unittests/Basic/SarifTest.cpp           |  2 +-
 llvm/include/llvm/Support/JSON.h              | 15 ++++++++++-----
 llvm/lib/Support/JSON.cpp                     |  4 ++--
 6 files changed, 23 insertions(+), 18 deletions(-)

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/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/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h
index 27862eb7ab6bb..7b7a7b29aca40 100644
--- a/llvm/include/llvm/Support/JSON.h
+++ b/llvm/include/llvm/Support/JSON.h
@@ -47,6 +47,7 @@
 #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"
@@ -93,10 +94,14 @@ class ObjectKey;
 class Value;
 template <typename T> Value toJSON(const std::optional<T> &Opt);
 
+struct ObjectKeyHash {
+  size_t operator()(const StringRef &K) const { return hash_value(K); }
+};
+
 /// An Object is a JSON object, which maps strings to heterogenous JSON values.
-/// It simulates DenseMap<ObjectKey, Value>. ObjectKey is a maybe-owned string.
+/// ObjectKey is a maybe-owned string.
 class Object {
-  using Storage = DenseMap<ObjectKey, Value, llvm::DenseMapInfo<StringRef>>;
+  using Storage = std::unordered_map<ObjectKey, Value, ObjectKeyHash>;
   Storage M;
 
 public:
@@ -133,8 +138,8 @@ class Object {
   bool erase(StringRef K);
   void erase(iterator I) { M.erase(I); }
 
-  iterator find(StringRef K) { return M.find_as(K); }
-  const_iterator find(StringRef K) const { return M.find_as(K); }
+  iterator find(const ObjectKey &K) { return M.find(K); }
+  const_iterator find(const ObjectKey &K) const { return M.find(K); }
   // operator[] acts as if Value was default-constructible as null.
   LLVM_ABI Value &operator[](const ObjectKey &K);
   LLVM_ABI Value &operator[](ObjectKey &&K);
@@ -646,7 +651,7 @@ inline Object::Object(std::initializer_list<KV> Properties) {
   for (const auto &P : Properties) {
     auto R = try_emplace(P.K, nullptr);
     if (R.second)
-      R.first->getSecond().moveFrom(std::move(P.V));
+      R.first->second.moveFrom(std::move(P.V));
   }
 }
 inline std::pair<Object::iterator, bool> Object::insert(KV E) {
diff --git a/llvm/lib/Support/JSON.cpp b/llvm/lib/Support/JSON.cpp
index 23c2542d75810..3124e6f0b8da8 100644
--- a/llvm/lib/Support/JSON.cpp
+++ b/llvm/lib/Support/JSON.cpp
@@ -22,10 +22,10 @@ namespace llvm {
 namespace json {
 
 Value &Object::operator[](const ObjectKey &K) {
-  return try_emplace(K, nullptr).first->getSecond();
+  return try_emplace(K, nullptr).first->second;
 }
 Value &Object::operator[](ObjectKey &&K) {
-  return try_emplace(std::move(K), nullptr).first->getSecond();
+  return try_emplace(std::move(K), nullptr).first->second;
 }
 Value *Object::get(StringRef K) {
   auto I = find(K);

>From 065f4bc0a67f62f5398088bfa4c9e8d876d3e0f5 Mon Sep 17 00:00:00 2001
From: Zixu Wang <zixu_wang at apple.com>
Date: Mon, 13 Apr 2026 17:24:05 -0700
Subject: [PATCH 2/3] [NFC] Update header includes for JSON.h

Remove unused DenseMap.h and Hashing.h includes from JSON.h after
replacing `DenseMap` with `std::unordered_map`.
Include missing AlignOf.h transitively included by DenseMap.h
Add missing DenseMap.h includes transitively brought in by JSON.h
---
 clang/include/clang/Basic/Sarif.h               | 1 +
 llvm/include/llvm/Support/JSON.h                | 4 ++--
 llvm/tools/llvm-mca/Views/InstructionInfoView.h | 1 +
 3 files changed, 4 insertions(+), 2 deletions(-)

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/llvm/include/llvm/Support/JSON.h b/llvm/include/llvm/Support/JSON.h
index 7b7a7b29aca40..d48cf3585a85d 100644
--- a/llvm/include/llvm/Support/JSON.h
+++ b/llvm/include/llvm/Support/JSON.h
@@ -46,17 +46,17 @@
 #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 {
diff --git a/llvm/tools/llvm-mca/Views/InstructionInfoView.h b/llvm/tools/llvm-mca/Views/InstructionInfoView.h
index 34c6fec46a6d5..a54d21528b72e 100644
--- a/llvm/tools/llvm-mca/Views/InstructionInfoView.h
+++ b/llvm/tools/llvm-mca/Views/InstructionInfoView.h
@@ -36,6 +36,7 @@
 
 #include "Views/InstructionView.h"
 #include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/MC/MCInst.h"
 #include "llvm/MC/MCInstPrinter.h"

>From 725ca51139d059d3485b3f85f8ee3002b414ef31 Mon Sep 17 00:00:00 2001
From: Zixu Wang <zixu_wang at apple.com>
Date: Mon, 13 Apr 2026 17:40:40 -0700
Subject: [PATCH 3/3] Add JSON parser benchmarks

Add benchmarks for LLVM's JSON parser.
Generates test JSON inputs with different sizes, and measures:
- Parsing speed
- Parsing memory consumption
- Accessing speed (iterating over parsed object, fetching values with
  sequential/randomized keys)

Assisted-by: Claude Code (claude-opus-4-6)
---
 llvm/benchmarks/CMakeLists.txt   |   1 +
 llvm/benchmarks/JSONParserBM.cpp | 288 +++++++++++++++++++++++++++++++
 2 files changed, 289 insertions(+)
 create mode 100644 llvm/benchmarks/JSONParserBM.cpp

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..3454d70f8c28e
--- /dev/null
+++ b/llvm/benchmarks/JSONParserBM.cpp
@@ -0,0 +1,288 @@
+//===- 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
+//
+//===----------------------------------------------------------------------===//
+
+#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
+//===----------------------------------------------------------------------===//
+
+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();



More information about the cfe-commits mailing list