Merge pull request #6395 from aws-lumberyard-dev/nvsickle/GenericDomDocument
Add a generic Value type to the Generic DOM
This commit is contained in:
@@ -8,9 +8,10 @@
|
||||
|
||||
#if defined(HAVE_BENCHMARK)
|
||||
|
||||
#include <AzCore/DOM/DomUtils.h>
|
||||
#include <AzCore/DOM/Backends/JSON/JsonBackend.h>
|
||||
#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
|
||||
#include <AzCore/DOM/DomUtils.h>
|
||||
#include <AzCore/DOM/DomValue.h>
|
||||
#include <AzCore/JSON/document.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
@@ -25,27 +26,31 @@ namespace Benchmark
|
||||
{
|
||||
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
|
||||
AZ::NameDictionary::Create();
|
||||
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Create();
|
||||
}
|
||||
|
||||
void SetUp(::benchmark::State& st) override
|
||||
{
|
||||
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
|
||||
AZ::NameDictionary::Create();
|
||||
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Create();
|
||||
}
|
||||
|
||||
void TearDown(::benchmark::State& st) override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Destroy();
|
||||
AZ::NameDictionary::Destroy();
|
||||
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& st) override
|
||||
{
|
||||
AZ::AllocatorInstance<AZ::Dom::ValueAllocator>::Destroy();
|
||||
AZ::NameDictionary::Destroy();
|
||||
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
|
||||
}
|
||||
|
||||
AZStd::string GenerateDomJsonBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength)
|
||||
rapidjson::Document GenerateDomJsonBenchmarkDocument(int64_t entryCount, int64_t stringTemplateLength)
|
||||
{
|
||||
rapidjson::Document document;
|
||||
document.SetObject();
|
||||
@@ -103,11 +108,28 @@ namespace Benchmark
|
||||
document.SetObject();
|
||||
document.AddMember("entries", createObject(), document.GetAllocator());
|
||||
|
||||
return document;
|
||||
}
|
||||
|
||||
AZStd::string GenerateDomJsonBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength)
|
||||
{
|
||||
rapidjson::Document document = GenerateDomJsonBenchmarkDocument(entryCount, stringTemplateLength);
|
||||
|
||||
AZStd::string serializedJson;
|
||||
auto result = AZ::JsonSerializationUtils::WriteJsonString(document, serializedJson);
|
||||
AZ_Assert(result.IsSuccess(), "Failed to serialize generated JSON");
|
||||
return serializedJson;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void TakeAndDiscardWithoutTimingDtor(T&& value, benchmark::State& state)
|
||||
{
|
||||
{
|
||||
T instance = AZStd::move(value);
|
||||
state.PauseTiming();
|
||||
}
|
||||
state.ResumeTiming();
|
||||
}
|
||||
};
|
||||
|
||||
// Helper macro for registering JSON benchmarks
|
||||
@@ -119,7 +141,7 @@ namespace Benchmark
|
||||
->Args({ 100, 500 }) \
|
||||
->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocumentInPlace)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)(benchmark::State& state)
|
||||
{
|
||||
AZ::Dom::JsonBackend backend;
|
||||
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
|
||||
@@ -136,14 +158,38 @@ namespace Benchmark
|
||||
return AZ::Dom::Utils::ReadFromStringInPlace(backend, payloadCopy, visitor);
|
||||
});
|
||||
|
||||
benchmark::DoNotOptimize(result.GetValue());
|
||||
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
|
||||
}
|
||||
|
||||
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocumentInPlace)
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocument)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)(benchmark::State& state)
|
||||
{
|
||||
AZ::Dom::JsonBackend backend;
|
||||
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
state.PauseTiming();
|
||||
AZStd::string payloadCopy = serializedPayload;
|
||||
state.ResumeTiming();
|
||||
|
||||
auto result = AZ::Dom::Utils::WriteToValue(
|
||||
[&](AZ::Dom::Visitor& visitor)
|
||||
{
|
||||
return AZ::Dom::Utils::ReadFromStringInPlace(backend, payloadCopy, visitor);
|
||||
});
|
||||
|
||||
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
|
||||
}
|
||||
|
||||
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjson)(benchmark::State& state)
|
||||
{
|
||||
AZ::Dom::JsonBackend backend;
|
||||
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
|
||||
@@ -156,14 +202,34 @@ namespace Benchmark
|
||||
return AZ::Dom::Utils::ReadFromString(backend, serializedPayload, AZ::Dom::Lifetime::Temporary, visitor);
|
||||
});
|
||||
|
||||
benchmark::DoNotOptimize(result.GetValue());
|
||||
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
|
||||
}
|
||||
|
||||
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocument)
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjson)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, JsonUtilsDeserializeToDocument)(benchmark::State& state)
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValue)(benchmark::State& state)
|
||||
{
|
||||
AZ::Dom::JsonBackend backend;
|
||||
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
auto result = AZ::Dom::Utils::WriteToValue(
|
||||
[&](AZ::Dom::Visitor& visitor)
|
||||
{
|
||||
return AZ::Dom::Utils::ReadFromString(backend, serializedPayload, AZ::Dom::Lifetime::Temporary, visitor);
|
||||
});
|
||||
|
||||
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
|
||||
}
|
||||
|
||||
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValue)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)(benchmark::State& state)
|
||||
{
|
||||
AZ::Dom::JsonBackend backend;
|
||||
AZStd::string serializedPayload = GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1));
|
||||
@@ -172,12 +238,78 @@ namespace Benchmark
|
||||
{
|
||||
auto result = AZ::JsonSerializationUtils::ReadJsonString(serializedPayload);
|
||||
|
||||
benchmark::DoNotOptimize(result.GetValue());
|
||||
TakeAndDiscardWithoutTimingDtor(result.TakeValue(), state);
|
||||
}
|
||||
|
||||
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, JsonUtilsDeserializeToDocument)
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonMakeComplexObject)(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state)
|
||||
{
|
||||
TakeAndDiscardWithoutTimingDtor(GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)), state);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonMakeComplexObject)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonLookupMemberByString)(benchmark::State& state)
|
||||
{
|
||||
rapidjson::Document document(rapidjson::kObjectType);
|
||||
AZStd::vector<AZStd::string> keys;
|
||||
for (int64_t i = 0; i < state.range(0); ++i)
|
||||
{
|
||||
AZStd::string key(AZStd::string::format("key%" PRId64, i));
|
||||
keys.push_back(key);
|
||||
document.AddMember(rapidjson::Value(key.data(), static_cast<rapidjson::SizeType>(key.size()), document.GetAllocator()), rapidjson::Value(i), document.GetAllocator());
|
||||
}
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const AZStd::string& key : keys)
|
||||
{
|
||||
benchmark::DoNotOptimize(document.FindMember(key.data()));
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * state.range(0));
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomJsonBenchmark, RapidjsonLookupMemberByString)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonDeepCopy)(benchmark::State& state)
|
||||
{
|
||||
rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
rapidjson::Document copy;
|
||||
copy.CopyFrom(original, copy.GetAllocator(), true);
|
||||
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeepCopy)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonCopyAndMutate)(benchmark::State& state)
|
||||
{
|
||||
rapidjson::Document original = GenerateDomJsonBenchmarkDocument(state.range(0), state.range(1));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
rapidjson::Document copy;
|
||||
copy.CopyFrom(original, copy.GetAllocator(), true);
|
||||
copy["entries"]["Key0"].PushBack(42, copy.GetAllocator());
|
||||
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonCopyAndMutate)
|
||||
|
||||
#undef BENCHMARK_REGISTER_JSON
|
||||
} // namespace Benchmark
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/DomValue.h>
|
||||
#include <AzCore/DOM/DomUtils.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <cinttypes>
|
||||
|
||||
namespace AZ::Dom::Benchmark
|
||||
{
|
||||
class DomValueBenchmark : public UnitTest::AllocatorsBenchmarkFixture
|
||||
{
|
||||
public:
|
||||
void SetUp(const ::benchmark::State& st) override
|
||||
{
|
||||
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
|
||||
AZ::NameDictionary::Create();
|
||||
AZ::AllocatorInstance<ValueAllocator>::Create();
|
||||
}
|
||||
|
||||
void SetUp(::benchmark::State& st) override
|
||||
{
|
||||
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
|
||||
AZ::NameDictionary::Create();
|
||||
AZ::AllocatorInstance<ValueAllocator>::Create();
|
||||
}
|
||||
|
||||
void TearDown(::benchmark::State& st) override
|
||||
{
|
||||
AZ::AllocatorInstance<ValueAllocator>::Destroy();
|
||||
AZ::NameDictionary::Destroy();
|
||||
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
|
||||
}
|
||||
|
||||
void TearDown(const ::benchmark::State& st) override
|
||||
{
|
||||
AZ::AllocatorInstance<ValueAllocator>::Destroy();
|
||||
AZ::NameDictionary::Destroy();
|
||||
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
|
||||
}
|
||||
|
||||
Value GenerateDomBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength)
|
||||
{
|
||||
Value root(Type::Object);
|
||||
|
||||
AZStd::string entryTemplate;
|
||||
while (entryTemplate.size() < static_cast<size_t>(stringTemplateLength))
|
||||
{
|
||||
entryTemplate += "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor ";
|
||||
}
|
||||
entryTemplate.resize(stringTemplateLength);
|
||||
AZStd::string buffer;
|
||||
|
||||
auto createString = [&](int n) -> Value
|
||||
{
|
||||
return Value(AZStd::string::format("#%i %s", n, entryTemplate.c_str()), true);
|
||||
};
|
||||
|
||||
auto createEntry = [&](int n) -> Value
|
||||
{
|
||||
Value entry(Type::Object);
|
||||
entry.AddMember("string", createString(n));
|
||||
entry.AddMember("int", Value(n));
|
||||
entry.AddMember("double", Value(static_cast<double>(n) * 0.5));
|
||||
entry.AddMember("bool", Value(n % 2 == 0));
|
||||
entry.AddMember("null", Value(Type::Null));
|
||||
return entry;
|
||||
};
|
||||
|
||||
auto createArray = [&]() -> Value
|
||||
{
|
||||
Value array(Type::Array);
|
||||
for (int i = 0; i < entryCount; ++i)
|
||||
{
|
||||
array.ArrayPushBack(createEntry(i));
|
||||
}
|
||||
return array;
|
||||
};
|
||||
|
||||
auto createObject = [&]() -> Value
|
||||
{
|
||||
Value object;
|
||||
object.SetObject();
|
||||
for (int i = 0; i < entryCount; ++i)
|
||||
{
|
||||
buffer = AZStd::string::format("Key%i", i);
|
||||
object.AddMember(AZ::Name(buffer), createArray());
|
||||
}
|
||||
return object;
|
||||
};
|
||||
|
||||
root["entries"] = createObject();
|
||||
|
||||
return root;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void TakeAndDiscardWithoutTimingDtor(T&& value, benchmark::State& state)
|
||||
{
|
||||
{
|
||||
T instance = AZStd::move(value);
|
||||
state.PauseTiming();
|
||||
}
|
||||
state.ResumeTiming();
|
||||
}
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueMakeComplexObject)(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state)
|
||||
{
|
||||
TakeAndDiscardWithoutTimingDtor(GenerateDomBenchmarkPayload(state.range(0), state.range(1)), state);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueMakeComplexObject)
|
||||
->Args({ 10, 5 })
|
||||
->Args({ 10, 500 })
|
||||
->Args({ 100, 5 })
|
||||
->Args({ 100, 500 })
|
||||
->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueShallowCopy)(benchmark::State& state)
|
||||
{
|
||||
Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
Value copy = original;
|
||||
benchmark::DoNotOptimize(copy);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueShallowCopy)
|
||||
->Args({ 10, 5 })
|
||||
->Args({ 10, 500 })
|
||||
->Args({ 100, 5 })
|
||||
->Args({ 100, 500 })
|
||||
->Unit(benchmark::kNanosecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueCopyAndMutate)(benchmark::State& state)
|
||||
{
|
||||
Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
Value copy = original;
|
||||
copy["entries"]["Key0"].ArrayPushBack(Value(42));
|
||||
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueCopyAndMutate)
|
||||
->Args({ 10, 5 })
|
||||
->Args({ 10, 500 })
|
||||
->Args({ 100, 5 })
|
||||
->Args({ 100, 500 })
|
||||
->Unit(benchmark::kNanosecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueDeepCopy)(benchmark::State& state)
|
||||
{
|
||||
Value original = GenerateDomBenchmarkPayload(state.range(0), state.range(1));
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
Value copy = Utils::DeepCopy(original);
|
||||
TakeAndDiscardWithoutTimingDtor(AZStd::move(copy), state);
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueDeepCopy)
|
||||
->Args({ 10, 5 })
|
||||
->Args({ 10, 500 })
|
||||
->Args({ 100, 5 })
|
||||
->Args({ 100, 500 })
|
||||
->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByName)(benchmark::State& state)
|
||||
{
|
||||
Value value(Type::Object);
|
||||
AZStd::vector<AZ::Name> keys;
|
||||
for (int64_t i = 0; i < state.range(0); ++i)
|
||||
{
|
||||
AZ::Name key(AZStd::string::format("key%" PRId64, i));
|
||||
keys.push_back(key);
|
||||
value[key] = i;
|
||||
}
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const AZ::Name& key : keys)
|
||||
{
|
||||
benchmark::DoNotOptimize(value[key]);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * state.range(0));
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByName)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByString)(benchmark::State& state)
|
||||
{
|
||||
Value value(Type::Object);
|
||||
AZStd::vector<AZStd::string> keys;
|
||||
for (int64_t i = 0; i < state.range(0); ++i)
|
||||
{
|
||||
AZStd::string key(AZStd::string::format("key%" PRId64, i));
|
||||
keys.push_back(key);
|
||||
value[key] = i;
|
||||
}
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const AZStd::string& key : keys)
|
||||
{
|
||||
benchmark::DoNotOptimize(value[key]);
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * state.range(0));
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByString)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByStringComparison)(benchmark::State& state)
|
||||
{
|
||||
Value value(Type::Object);
|
||||
AZStd::vector<AZStd::string> keys;
|
||||
for (int64_t i = 0; i < state.range(0); ++i)
|
||||
{
|
||||
AZStd::string key(AZStd::string::format("key%" PRId64, i));
|
||||
keys.push_back(key);
|
||||
value[key] = i;
|
||||
}
|
||||
|
||||
for (auto _ : state)
|
||||
{
|
||||
for (const AZStd::string& key : keys)
|
||||
{
|
||||
const Object::ContainerType& object = value.GetObject();
|
||||
benchmark::DoNotOptimize(AZStd::find_if(
|
||||
object.cbegin(), object.cend(),
|
||||
[&key](const Object::EntryType& entry)
|
||||
{
|
||||
return key == entry.first.GetStringView();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.iterations() * state.range(0));
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, LookupMemberByStringComparison)->Arg(100)->Arg(1000)->Arg(10000)->Unit(benchmark::kMillisecond);
|
||||
|
||||
} // namespace AZ::Dom::Benchmark
|
||||
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AzCore/DOM/Backends/JSON/JsonBackend.h>
|
||||
#include <AzCore/DOM/Backends/JSON/JsonSerializationUtils.h>
|
||||
#include <AzCore/DOM/DomUtils.h>
|
||||
#include <AzCore/DOM/DomValue.h>
|
||||
#include <AzCore/Name/NameDictionary.h>
|
||||
#include <AzCore/Serialization/Json/JsonSerialization.h>
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
#include <AzCore/UnitTest/TestTypes.h>
|
||||
#include <AzCore/std/numeric.h>
|
||||
|
||||
namespace AZ::Dom::Tests
|
||||
{
|
||||
class DomValueTests : public UnitTest::AllocatorsFixture
|
||||
{
|
||||
public:
|
||||
void SetUp() override
|
||||
{
|
||||
UnitTest::AllocatorsFixture::SetUp();
|
||||
NameDictionary::Create();
|
||||
AZ::AllocatorInstance<ValueAllocator>::Create();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_value = Value();
|
||||
|
||||
AZ::AllocatorInstance<ValueAllocator>::Destroy();
|
||||
NameDictionary::Destroy();
|
||||
UnitTest::AllocatorsFixture::TearDown();
|
||||
}
|
||||
|
||||
void PerformValueChecks()
|
||||
{
|
||||
Value shallowCopy = m_value;
|
||||
EXPECT_EQ(m_value, shallowCopy);
|
||||
EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, shallowCopy));
|
||||
|
||||
Value deepCopy = Utils::DeepCopy(m_value);
|
||||
EXPECT_TRUE(Utils::DeepCompareIsEqual(m_value, deepCopy));
|
||||
}
|
||||
|
||||
Value m_value;
|
||||
};
|
||||
|
||||
TEST_F(DomValueTests, EmptyArray)
|
||||
{
|
||||
m_value.SetArray();
|
||||
|
||||
EXPECT_TRUE(m_value.IsArray());
|
||||
EXPECT_EQ(m_value.ArraySize(), 0);
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, SimpleArray)
|
||||
{
|
||||
m_value.SetArray();
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
m_value.ArrayPushBack(Value(i));
|
||||
EXPECT_EQ(m_value.ArraySize(), i + 1);
|
||||
EXPECT_EQ(m_value[i].GetInt64(), i);
|
||||
}
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, NestedArrays)
|
||||
{
|
||||
Value x(5);
|
||||
m_value.SetArray();
|
||||
for (int j = 0; j < 5; ++j)
|
||||
{
|
||||
Value nestedArray(Type::Array);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
nestedArray.ArrayPushBack(Value(i));
|
||||
}
|
||||
m_value.ArrayPushBack(AZStd::move(nestedArray));
|
||||
}
|
||||
|
||||
EXPECT_EQ(m_value.ArraySize(), 5);
|
||||
for (int i = 0; i < 3; ++i)
|
||||
{
|
||||
EXPECT_EQ(m_value[i].ArraySize(), 5);
|
||||
for (int j = 0; j < 5; ++j)
|
||||
{
|
||||
EXPECT_EQ(m_value[i][j].GetInt64(), j);
|
||||
}
|
||||
}
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, EmptyObject)
|
||||
{
|
||||
m_value.SetObject();
|
||||
EXPECT_EQ(m_value.MemberCount(), 0);
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, SimpleObject)
|
||||
{
|
||||
m_value.SetObject();
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
AZStd::string key = AZStd::string::format("Key%i", i);
|
||||
m_value.AddMember(key, Value(i));
|
||||
EXPECT_EQ(m_value.MemberCount(), i + 1);
|
||||
EXPECT_EQ(m_value[key].GetInt64(), i);
|
||||
}
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, NestedObjects)
|
||||
{
|
||||
m_value.SetObject();
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
Value nestedObject(Type::Object);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
nestedObject.AddMember(AZStd::string::format("Key%i", i), Value(i));
|
||||
}
|
||||
m_value.AddMember(AZStd::string::format("Obj%i", j), AZStd::move(nestedObject));
|
||||
}
|
||||
|
||||
EXPECT_EQ(m_value.MemberCount(), 3);
|
||||
for (int j = 0; j < 3; ++j)
|
||||
{
|
||||
const Value& nestedObject = m_value[AZStd::string::format("Obj%i", j)];
|
||||
EXPECT_EQ(nestedObject.MemberCount(), 5);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
EXPECT_EQ(nestedObject[AZStd::string::format("Key%i", i)].GetInt64(), i);
|
||||
}
|
||||
}
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, EmptyNode)
|
||||
{
|
||||
m_value.SetNode("Test");
|
||||
EXPECT_EQ(m_value.GetNodeName(), AZ::Name("Test"));
|
||||
EXPECT_EQ(m_value.MemberCount(), 0);
|
||||
EXPECT_EQ(m_value.ArraySize(), 0);
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, SimpleNode)
|
||||
{
|
||||
m_value.SetNode("Test");
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
m_value.ArrayPushBack(Value(i));
|
||||
EXPECT_EQ(m_value.ArraySize(), i + 1);
|
||||
EXPECT_EQ(m_value[i].GetInt64(), i);
|
||||
|
||||
if (i < 5)
|
||||
{
|
||||
AZ::Name key = AZ::Name(AZStd::string::format("TwoTimes%i", i));
|
||||
m_value.AddMember(key, Value(i * 2));
|
||||
EXPECT_EQ(m_value.MemberCount(), i + 1);
|
||||
EXPECT_EQ(m_value[key].GetInt64(), i * 2);
|
||||
}
|
||||
}
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, NestedNodes)
|
||||
{
|
||||
m_value.SetNode("TopLevel");
|
||||
|
||||
const AZ::Name childNodeName("ChildNode");
|
||||
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
Value childNode(Type::Node);
|
||||
childNode.SetNodeName(childNodeName);
|
||||
childNode.SetNodeValue(Value(i));
|
||||
|
||||
childNode.AddMember("foo", Value(i));
|
||||
childNode.AddMember("bar", Value("test", false));
|
||||
|
||||
m_value.ArrayPushBack(childNode);
|
||||
}
|
||||
|
||||
EXPECT_EQ(m_value.ArraySize(), 5);
|
||||
for (int i = 0; i < 5; ++i)
|
||||
{
|
||||
const Value& childNode = m_value[i];
|
||||
EXPECT_EQ(childNode.GetNodeName(), childNodeName);
|
||||
EXPECT_EQ(childNode.GetNodeValue().GetInt64(), i);
|
||||
EXPECT_EQ(childNode["foo"].GetInt64(), i);
|
||||
EXPECT_EQ(childNode["bar"].GetString(), "test");
|
||||
}
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, Int64)
|
||||
{
|
||||
m_value.SetObject();
|
||||
m_value["int64_min"] = AZStd::numeric_limits<int64_t>::min();
|
||||
m_value["int64_max"] = AZStd::numeric_limits<int64_t>::max();
|
||||
|
||||
EXPECT_EQ(m_value["int64_min"].GetType(), Type::Int64);
|
||||
EXPECT_EQ(m_value["int64_min"].GetInt64(), AZStd::numeric_limits<int64_t>::min());
|
||||
EXPECT_EQ(m_value["int64_max"].GetType(), Type::Int64);
|
||||
EXPECT_EQ(m_value["int64_max"].GetInt64(), AZStd::numeric_limits<int64_t>::max());
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, Uint64)
|
||||
{
|
||||
m_value.SetObject();
|
||||
m_value["uint64_min"] = AZStd::numeric_limits<uint64_t>::min();
|
||||
m_value["uint64_max"] = AZStd::numeric_limits<uint64_t>::max();
|
||||
|
||||
EXPECT_EQ(m_value["uint64_min"].GetType(), Type::Uint64);
|
||||
EXPECT_EQ(m_value["uint64_min"].GetInt64(), AZStd::numeric_limits<uint64_t>::min());
|
||||
EXPECT_EQ(m_value["uint64_max"].GetType(), Type::Uint64);
|
||||
EXPECT_EQ(m_value["uint64_max"].GetInt64(), AZStd::numeric_limits<uint64_t>::max());
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, Double)
|
||||
{
|
||||
m_value.SetObject();
|
||||
m_value["double_min"] = AZStd::numeric_limits<double>::min();
|
||||
m_value["double_max"] = AZStd::numeric_limits<double>::max();
|
||||
|
||||
EXPECT_EQ(m_value["double_min"].GetType(), Type::Double);
|
||||
EXPECT_EQ(m_value["double_min"].GetDouble(), AZStd::numeric_limits<double>::min());
|
||||
EXPECT_EQ(m_value["double_max"].GetType(), Type::Double);
|
||||
EXPECT_EQ(m_value["double_max"].GetDouble(), AZStd::numeric_limits<double>::max());
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, Null)
|
||||
{
|
||||
m_value.SetObject();
|
||||
m_value["null_value"] = Value(Type::Null);
|
||||
|
||||
EXPECT_EQ(m_value["null_value"].GetType(), Type::Null);
|
||||
EXPECT_EQ(m_value["null_type"], Value());
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, Bool)
|
||||
{
|
||||
m_value.SetObject();
|
||||
m_value["true_value"] = true;
|
||||
m_value["false_value"] = false;
|
||||
|
||||
EXPECT_EQ(m_value["true_value"].GetType(), Type::Bool);
|
||||
EXPECT_EQ(m_value["true_value"].GetBool(), true);
|
||||
EXPECT_EQ(m_value["false_value"].GetType(), Type::Bool);
|
||||
EXPECT_EQ(m_value["false_value"].GetBool(), false);
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, String)
|
||||
{
|
||||
const char* s1 = "reference string long enough to avoid SSO";
|
||||
const char* s2 = "copy string long enough to avoid SSO";
|
||||
|
||||
m_value.SetObject();
|
||||
AZStd::string stringToReference = s1;
|
||||
m_value["no_copy"] = Value(stringToReference, false);
|
||||
AZStd::string stringToCopy = s2;
|
||||
m_value["copy"] = Value(stringToCopy, true);
|
||||
|
||||
EXPECT_EQ(m_value["no_copy"].GetType(), Type::String);
|
||||
EXPECT_EQ(m_value["no_copy"].GetString(), s1);
|
||||
stringToReference.at(0) = 'F';
|
||||
EXPECT_NE(m_value["no_copy"].GetString(), s1);
|
||||
|
||||
EXPECT_EQ(m_value["copy"].GetType(), Type::String);
|
||||
EXPECT_EQ(m_value["copy"].GetString(), s2);
|
||||
stringToCopy.at(0) = 'F';
|
||||
EXPECT_EQ(m_value["copy"].GetString(), s2);
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, CopyOnWrite_Object)
|
||||
{
|
||||
Value v1(Type::Object);
|
||||
v1["foo"] = 5;
|
||||
|
||||
Value nestedObject(Type::Object);
|
||||
v1["obj"] = nestedObject;
|
||||
|
||||
Value v2 = v1;
|
||||
EXPECT_EQ(&v1.GetObject(), &v2.GetObject());
|
||||
EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
|
||||
|
||||
v2["foo"] = 0;
|
||||
|
||||
EXPECT_NE(&v1.GetObject(), &v2.GetObject());
|
||||
EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
|
||||
|
||||
v2["obj"]["key"] = true;
|
||||
|
||||
EXPECT_NE(&v1.GetObject(), &v2.GetObject());
|
||||
EXPECT_NE(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
|
||||
|
||||
v2 = v1;
|
||||
|
||||
EXPECT_EQ(&v1.GetObject(), &v2.GetObject());
|
||||
EXPECT_EQ(&v1.FindMember("obj")->second.GetObject(), &v2.FindMember("obj")->second.GetObject());
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, CopyOnWrite_Array)
|
||||
{
|
||||
Value v1(Type::Array);
|
||||
v1.ArrayPushBack(Value(1));
|
||||
v1.ArrayPushBack(Value(2));
|
||||
|
||||
Value nestedArray(Type::Array);
|
||||
v1.ArrayPushBack(nestedArray);
|
||||
Value v2 = v1;
|
||||
|
||||
EXPECT_EQ(&v1.GetArray(), &v2.GetArray());
|
||||
EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
|
||||
|
||||
v2[0] = 0;
|
||||
|
||||
EXPECT_NE(&v1.GetArray(), &v2.GetArray());
|
||||
EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
|
||||
|
||||
v2[2].ArrayPushBack(Value(42));
|
||||
|
||||
EXPECT_NE(&v1.GetArray(), &v2.GetArray());
|
||||
EXPECT_NE(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
|
||||
|
||||
v2 = v1;
|
||||
|
||||
EXPECT_EQ(&v1.GetArray(), &v2.GetArray());
|
||||
EXPECT_EQ(&v1.ArrayAt(2).GetArray(), &v2.ArrayAt(2).GetArray());
|
||||
}
|
||||
|
||||
TEST_F(DomValueTests, CopyOnWrite_Node)
|
||||
{
|
||||
Value v1;
|
||||
v1.SetNode("TopLevel");
|
||||
|
||||
v1.ArrayPushBack(Value(1));
|
||||
v1.ArrayPushBack(Value(2));
|
||||
v1["obj"].SetNode("Nested");
|
||||
Value v2 = v1;
|
||||
|
||||
EXPECT_EQ(&v1.GetNode(), &v2.GetNode());
|
||||
EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode());
|
||||
|
||||
v2[0] = 0;
|
||||
|
||||
EXPECT_NE(&v1.GetNode(), &v2.GetNode());
|
||||
EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode());
|
||||
|
||||
v2["obj"].ArrayPushBack(Value(42));
|
||||
|
||||
EXPECT_NE(&v1.GetNode(), &v2.GetNode());
|
||||
EXPECT_NE(&v1["obj"].GetNode(), &v2["obj"].GetNode());
|
||||
|
||||
v2 = v1;
|
||||
|
||||
EXPECT_EQ(&v1.GetNode(), &v2.GetNode());
|
||||
EXPECT_EQ(&v1["obj"].GetNode(), &v2["obj"].GetNode());
|
||||
}
|
||||
} // namespace AZ::Dom::Tests
|
||||
@@ -217,6 +217,8 @@ set(FILES
|
||||
AZStd/VectorAndArray.cpp
|
||||
DOM/DomJsonTests.cpp
|
||||
DOM/DomJsonBenchmarks.cpp
|
||||
DOM/DomValueTests.cpp
|
||||
DOM/DomValueBenchmarks.cpp
|
||||
)
|
||||
|
||||
# Prevent the following files from being grouped in UNITY builds
|
||||
|
||||
Reference in New Issue
Block a user