Revert "[Support][JSON] Use std::unordered_map for object storage" (#193549)

Reverts llvm/llvm-project#171230 to look at failures.
This commit is contained in:
Zixu Wang
2026-04-22 10:33:54 -07:00
committed by GitHub
parent 7136a4b39b
commit 7a633290d6
12 changed files with 19 additions and 333 deletions

View File

@@ -155,7 +155,7 @@ static void insertComment(Object &Description, json::Value &Comment,
Description[Key] = std::move(CommentsArray);
Description["Has" + Key.str()] = true;
} else {
DescriptionIt->second.getAsArray()->push_back(Comment);
DescriptionIt->getSecond().getAsArray()->push_back(Comment);
}
}

View File

@@ -34,7 +34,6 @@
#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"

View File

@@ -25,7 +25,7 @@ std::optional<VersionTuple> DarwinSDKInfo::RelatedTargetVersionMapping::map(
return MaximumValue;
auto KV = Mapping.find(Key.normalize());
if (KV != Mapping.end())
return KV->second;
return KV->getSecond();
// 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.second.getAsString()) {
if (auto Val = KV.getSecond().getAsString()) {
llvm::VersionTuple KeyVersion;
llvm::VersionTuple ValueVersion;
if (KeyVersion.tryParse(KV.first) || ValueVersion.tryParse(*Val))
if (KeyVersion.tryParse(KV.getFirst()) || 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.second.getAsObject();
SupportedTargetPair.getSecond().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.first;
StringRef PlatformOrVariant = SupportedTargetPair.getFirst();
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.first).split("_");
auto Pair = StringRef(KV.getFirst()).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.second.getAsObject(), *MaximumDeploymentVersion);
*KV.getSecond().getAsObject(), *MaximumDeploymentVersion);
if (Mapping)
VersionMappings[OSEnvPair(llvm::Triple::IOS,
llvm::Triple::UnknownEnvironment,

View File

@@ -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.second.getAsArray();
std::string Label = "-X" + KV.first.str();
const Array *ArgList = KV.getSecond().getAsArray();
std::string Label = "-X" + KV.getFirst().str();
if (!ArgList)
return make_error<TextAPIError>(TextAPIErrorCode::InvalidInputFormat);
for (auto Arg : *ArgList) {

View File

@@ -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.first; });
[](auto Item) { return Item.getFirst(); });
// THEN:
ASSERT_THAT(Keys, testing::UnorderedElementsAre("$schema", "version"));

View File

@@ -12,8 +12,6 @@
#include "lldb/Target/DynamicLoader.h"
#include "lldb/lldb-forward.h"
#include "llvm/ADT/DenseMap.h"
namespace lldb_private {
class DynamicLoaderWindowsDYLD : public DynamicLoader {

View File

@@ -10,7 +10,6 @@
#define liblldb_IntelPTPerThreadTraceCollection_H_
#include "IntelPTSingleBufferTrace.h"
#include "llvm/ADT/DenseMap.h"
#include <optional>
namespace lldb_private {

View File

@@ -15,7 +15,6 @@ 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)

View File

@@ -1,299 +0,0 @@
//===- 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();

View File

@@ -46,17 +46,16 @@
#ifndef LLVM_SUPPORT_JSON_H
#define LLVM_SUPPORT_JSON_H
#include "llvm/ADT/DenseMap.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 {
@@ -95,14 +94,9 @@ class Value;
template <typename T> Value toJSON(const std::optional<T> &Opt);
/// An Object is a JSON object, which maps strings to heterogenous JSON values.
/// ObjectKey is a maybe-owned string.
/// It simulates DenseMap<ObjectKey, Value>. ObjectKey is a maybe-owned string.
class Object {
struct ObjectKeyHash {
template <typename T> size_t operator()(const T &Key) const {
return hash_value(Key);
}
};
using Storage = std::unordered_map<ObjectKey, Value, ObjectKeyHash>;
using Storage = DenseMap<ObjectKey, Value, llvm::DenseMapInfo<StringRef>>;
Storage M;
public:
@@ -139,11 +133,8 @@ public:
bool erase(StringRef K);
void erase(iterator I) { M.erase(I); }
// TODO: Implement heterogeneous lookup using StringRef directly. We need to
// make ObjectKey transparent with transparent hash and key equality check.
// This is supported for unordered containers in C++20.
iterator find(const ObjectKey &K) { return M.find(K); }
const_iterator find(const ObjectKey &K) const { return M.find(K); }
iterator find(StringRef K) { return M.find_as(K); }
const_iterator find(StringRef K) const { return M.find_as(K); }
// operator[] acts as if Value was default-constructible as null.
LLVM_ABI Value &operator[](const ObjectKey &K);
LLVM_ABI Value &operator[](ObjectKey &&K);
@@ -655,7 +646,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->second.moveFrom(std::move(P.V));
R.first->getSecond().moveFrom(std::move(P.V));
}
}
inline std::pair<Object::iterator, bool> Object::insert(KV E) {

View File

@@ -22,10 +22,10 @@ namespace llvm {
namespace json {
Value &Object::operator[](const ObjectKey &K) {
return try_emplace(K, nullptr).first->second;
return try_emplace(K, nullptr).first->getSecond();
}
Value &Object::operator[](ObjectKey &&K) {
return try_emplace(std::move(K), nullptr).first->second;
return try_emplace(std::move(K), nullptr).first->getSecond();
}
Value *Object::get(StringRef K) {
auto I = find(K);

View File

@@ -36,7 +36,6 @@
#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"