A bit of Generic DOM tidying/fixup (#6914)

* A bit of Generic DOM tidying/fixup

- Refactor out a test fixture for all DOM tests / benchmarks
- Optimize `GetType` implementation to not use `AZStd::variant::visit` (benchmark included to A/B the implementations)
- Tag a few more mutating Value functions with "Mutable" to avoid astonishing copy-on-writes

Benchmark results for GetType implementation:
```
DomValueBenchmark/AzDomValueGetType_UsingVariantIndex              18.2 ns         18.0 ns     40727273 items_per_second=443.667M/s
DomValueBenchmark/AzDomValueGetType_UsingVariantVisit              32.2 ns         32.2 ns     21333333 items_per_second=248.242M/s
```

Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
Nicholas Van Sickle
2022-01-19 11:52:57 -08:00
committed by GitHub
parent 8ec07031e3
commit cfd721bce1
10 changed files with 453 additions and 345 deletions
@@ -77,8 +77,8 @@ namespace AZ::Dom::Utils
for (size_t i = 0; i < ourValues.size(); ++i)
{
const Object::EntryType& lhsChild = ourValues[i];
const Object::EntryType& rhsChild = theirValues[i];
if (lhsChild.first != rhsChild.first || !DeepCompareIsEqual(lhsChild.second, rhsChild.second))
auto rhsIt = rhs.FindMember(lhsChild.first);
if (rhsIt == rhs.MemberEnd() || !DeepCompareIsEqual(lhsChild.second, rhsIt->second))
{
return false;
}
@@ -144,8 +144,8 @@ namespace AZ::Dom::Utils
for (size_t i = 0; i < ourProperties.size(); ++i)
{
const Object::EntryType& lhsChild = ourProperties[i];
const Object::EntryType& rhsChild = theirProperties[i];
if (lhsChild.first != rhsChild.first || !DeepCompareIsEqual(lhsChild.second, rhsChild.second))
auto rhsIt = rhs.FindMember(lhsChild.first);
if (rhsIt == rhs.MemberEnd() || !DeepCompareIsEqual(lhsChild.second, rhsIt->second))
{
return false;
}
+39 -66
View File
@@ -283,64 +283,33 @@ namespace AZ::Dom
Type Dom::Value::GetType() const
{
return AZStd::visit(
[](auto&& value) -> Type
{
using CurrentType = AZStd::decay_t<decltype(value)>;
if constexpr (AZStd::is_same_v<CurrentType, AZStd::monostate>)
{
return Type::Null;
}
else if constexpr (AZStd::is_same_v<CurrentType, int64_t>)
{
return Type::Int64;
}
else if constexpr (AZStd::is_same_v<CurrentType, uint64_t>)
{
return Type::Uint64;
}
else if constexpr (AZStd::is_same_v<CurrentType, double>)
{
return Type::Double;
}
else if constexpr (AZStd::is_same_v<CurrentType, bool>)
{
return Type::Bool;
}
else if constexpr (AZStd::is_same_v<CurrentType, AZStd::string_view>)
{
return Type::String;
}
else if constexpr (AZStd::is_same_v<CurrentType, SharedStringType>)
{
return Type::String;
}
else if constexpr (AZStd::is_same_v<CurrentType, ShortStringType>)
{
return Type::String;
}
else if constexpr (AZStd::is_same_v<CurrentType, ObjectPtr>)
{
return Type::Object;
}
else if constexpr (AZStd::is_same_v<CurrentType, ArrayPtr>)
{
return Type::Array;
}
else if constexpr (AZStd::is_same_v<CurrentType, NodePtr>)
{
return Type::Node;
}
else if constexpr (AZStd::is_same_v<CurrentType, OpaqueStorageType>)
{
return Type::Opaque;
}
else
{
AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type");
}
},
m_value);
switch (m_value.index())
{
case GetTypeIndex<AZStd::monostate>():
return Type::Null;
case GetTypeIndex<int64_t>():
return Type::Int64;
case GetTypeIndex<uint64_t>():
return Type::Uint64;
case GetTypeIndex<double>():
return Type::Double;
case GetTypeIndex<bool>():
return Type::Bool;
case GetTypeIndex<AZStd::string_view>():
case GetTypeIndex<SharedStringType>():
case GetTypeIndex<ShortStringType>():
return Type::String;
case GetTypeIndex<ObjectPtr>():
return Type::Object;
case GetTypeIndex<ArrayPtr>():
return Type::Array;
case GetTypeIndex<NodePtr>():
return Type::Node;
case GetTypeIndex<AZStd::shared_ptr<AZStd::any>>():
return Type::Opaque;
}
AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type");
return Type::Null;
}
bool Value::IsNull() const
@@ -594,12 +563,12 @@ namespace AZ::Dom
return GetObjectInternal().end();
}
Object::Iterator Value::MemberBegin()
Object::Iterator Value::MutableMemberBegin()
{
return GetObjectInternal().begin();
}
Object::Iterator Value::MemberEnd()
Object::Iterator Value::MutableMemberEnd()
{
return GetObjectInternal().end();
}
@@ -725,12 +694,12 @@ namespace AZ::Dom
return object.end();
}
Object::Iterator Value::EraseMember(Object::ConstIterator pos)
Object::Iterator Value::EraseMember(Object::Iterator pos)
{
return GetObjectInternal().erase(pos);
}
Object::Iterator Value::EraseMember(Object::ConstIterator first, Object::ConstIterator last)
Object::Iterator Value::EraseMember(Object::Iterator first, Object::Iterator last)
{
return GetObjectInternal().erase(first, last);
}
@@ -811,12 +780,12 @@ namespace AZ::Dom
return GetArrayInternal().end();
}
Array::Iterator Value::ArrayBegin()
Array::Iterator Value::MutableArrayBegin()
{
return GetArrayInternal().begin();
}
Array::Iterator Value::ArrayEnd()
Array::Iterator Value::MutableArrayEnd()
{
return GetArrayInternal().end();
}
@@ -843,12 +812,12 @@ namespace AZ::Dom
return *this;
}
Array::Iterator Value::ArrayErase(Array::ConstIterator pos)
Array::Iterator Value::ArrayErase(Array::Iterator pos)
{
return GetArrayInternal().erase(pos);
}
Array::Iterator Value::ArrayErase(Array::ConstIterator first, Array::ConstIterator last)
Array::Iterator Value::ArrayErase(Array::Iterator first, Array::Iterator last)
{
return GetArrayInternal().erase(first, last);
}
@@ -1113,6 +1082,10 @@ namespace AZ::Dom
{
result = visitor.RefCountedString(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent);
}
else if constexpr (AZStd::is_same_v<Alternative, ShortStringType>)
{
result = visitor.String(arg, copyStrings ? Lifetime::Temporary : Lifetime::Persistent);
}
else if constexpr (AZStd::is_same_v<Alternative, ObjectPtr>)
{
result = visitor.StartObject();
+8 -8
View File
@@ -268,8 +268,8 @@ namespace AZ::Dom
Object::ConstIterator MemberBegin() const;
Object::ConstIterator MemberEnd() const;
Object::Iterator MemberBegin();
Object::Iterator MemberEnd();
Object::Iterator MutableMemberBegin();
Object::Iterator MutableMemberEnd();
Object::Iterator FindMutableMember(KeyType name);
Object::Iterator FindMutableMember(AZStd::string_view name);
@@ -289,8 +289,8 @@ namespace AZ::Dom
void RemoveMember(KeyType name);
void RemoveMember(AZStd::string_view name);
Object::Iterator RemoveMember(Object::Iterator pos);
Object::Iterator EraseMember(Object::ConstIterator pos);
Object::Iterator EraseMember(Object::ConstIterator first, Object::ConstIterator last);
Object::Iterator EraseMember(Object::Iterator pos);
Object::Iterator EraseMember(Object::Iterator first, Object::Iterator last);
Object::Iterator EraseMember(KeyType name);
Object::Iterator EraseMember(AZStd::string_view name);
@@ -313,15 +313,15 @@ namespace AZ::Dom
Array::ConstIterator ArrayBegin() const;
Array::ConstIterator ArrayEnd() const;
Array::Iterator ArrayBegin();
Array::Iterator ArrayEnd();
Array::Iterator MutableArrayBegin();
Array::Iterator MutableArrayEnd();
Value& ArrayReserve(size_t newCapacity);
Value& ArrayPushBack(Value value);
Value& ArrayPopBack();
Array::Iterator ArrayErase(Array::ConstIterator pos);
Array::Iterator ArrayErase(Array::ConstIterator first, Array::ConstIterator last);
Array::Iterator ArrayErase(Array::Iterator pos);
Array::Iterator ArrayErase(Array::Iterator first, Array::Iterator last);
Array::ContainerType& GetMutableArray();
const Array::ContainerType& GetArray() const;
@@ -0,0 +1,189 @@
/*
* 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/Name/NameDictionary.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <Tests/DOM/DomFixtures.h>
namespace AZ::Dom::Tests
{
void DomTestHarness::SetUpHarness()
{
NameDictionary::Create();
AZ::AllocatorInstance<ValueAllocator>::Create();
}
void DomTestHarness::TearDownHarness()
{
AZ::AllocatorInstance<ValueAllocator>::Destroy();
NameDictionary::Destroy();
}
void DomBenchmarkFixture::SetUp(const ::benchmark::State& st)
{
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
SetUpHarness();
}
void DomBenchmarkFixture::SetUp(::benchmark::State& st)
{
UnitTest::AllocatorsBenchmarkFixture::SetUp(st);
SetUpHarness();
}
void DomBenchmarkFixture::TearDown(::benchmark::State& st)
{
TearDownHarness();
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
}
void DomBenchmarkFixture::TearDown(const ::benchmark::State& st)
{
TearDownHarness();
UnitTest::AllocatorsBenchmarkFixture::TearDown(st);
}
rapidjson::Document DomBenchmarkFixture::GenerateDomJsonBenchmarkDocument(int64_t entryCount, int64_t stringTemplateLength)
{
rapidjson::Document document;
document.SetObject();
AZStd::string entryTemplate;
while (entryTemplate.size() < aznumeric_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) -> rapidjson::Value
{
buffer = AZStd::string::format("#%i %s", n, entryTemplate.c_str());
return rapidjson::Value(buffer.data(), aznumeric_cast<rapidjson::SizeType>(buffer.size()), document.GetAllocator());
};
auto createEntry = [&](int n) -> rapidjson::Value
{
rapidjson::Value entry(rapidjson::kObjectType);
entry.AddMember("string", createString(n), document.GetAllocator());
entry.AddMember("int", rapidjson::Value(n), document.GetAllocator());
entry.AddMember("double", rapidjson::Value(aznumeric_cast<double>(n) * 0.5), document.GetAllocator());
entry.AddMember("bool", rapidjson::Value(n % 2 == 0), document.GetAllocator());
entry.AddMember("null", rapidjson::Value(rapidjson::kNullType), document.GetAllocator());
return entry;
};
auto createArray = [&]() -> rapidjson::Value
{
rapidjson::Value array;
array.SetArray();
for (int i = 0; i < entryCount; ++i)
{
array.PushBack(createEntry(i), document.GetAllocator());
}
return array;
};
auto createObject = [&]() -> rapidjson::Value
{
rapidjson::Value object;
object.SetObject();
for (int i = 0; i < entryCount; ++i)
{
buffer = AZStd::string::format("Key%i", i);
rapidjson::Value key;
key.SetString(buffer.data(), aznumeric_cast<rapidjson::SizeType>(buffer.length()), document.GetAllocator());
object.AddMember(key.Move(), createArray(), document.GetAllocator());
}
return object;
};
document.SetObject();
document.AddMember("entries", createObject(), document.GetAllocator());
return document;
}
AZStd::string DomBenchmarkFixture::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;
}
Value DomBenchmarkFixture::GenerateDomBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength)
{
Value root(Type::Object);
AZStd::string entryTemplate;
while (entryTemplate.size() < aznumeric_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(aznumeric_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;
}
void DomTestFixture::SetUp()
{
UnitTest::AllocatorsFixture::SetUp();
SetUpHarness();
}
void DomTestFixture::TearDown()
{
TearDownHarness();
UnitTest::AllocatorsFixture::TearDown();
}
} // namespace AZ::Dom::Tests
@@ -0,0 +1,66 @@
/*
* 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
*
*/
#pragma once
#include <AzCore/DOM/DomUtils.h>
#include <AzCore/JSON/document.h>
#include <AzCore/UnitTest/TestTypes.h>
#define DOM_REGISTER_SERIALIZATION_BENCHMARK(BaseClass, Method) \
BENCHMARK_REGISTER_F(BaseClass, Method)->Args({ 10, 5 })->Args({ 10, 500 })->Args({ 100, 5 })->Args({ 100, 500 })
#define DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(BaseClass, Method) \
DOM_REGISTER_SERIALIZATION_BENCHMARK(BaseClass, Method)->Unit(benchmark::kMillisecond);
#define DOM_REGISTER_SERIALIZATION_BENCHMARK_NS(BaseClass, Method) \
DOM_REGISTER_SERIALIZATION_BENCHMARK(BaseClass, Method)->Unit(benchmark::kNanosecond);
namespace AZ::Dom::Tests
{
class DomTestHarness
{
public:
virtual ~DomTestHarness() = default;
virtual void SetUpHarness();
virtual void TearDownHarness();
};
class DomBenchmarkFixture
: public DomTestHarness
, public UnitTest::AllocatorsBenchmarkFixture
{
public:
void SetUp(const ::benchmark::State& st) override;
void SetUp(::benchmark::State& st) override;
void TearDown(::benchmark::State& st) override;
void TearDown(const ::benchmark::State& st) override;
rapidjson::Document GenerateDomJsonBenchmarkDocument(int64_t entryCount, int64_t stringTemplateLength);
AZStd::string GenerateDomJsonBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength);
Value GenerateDomBenchmarkPayload(int64_t entryCount, int64_t stringTemplateLength);
template<class T>
static void TakeAndDiscardWithoutTimingDtor(T&& value, benchmark::State& state)
{
{
T instance = AZStd::move(value);
state.PauseTiming();
}
state.ResumeTiming();
}
};
class DomTestFixture
: public DomTestHarness
, public UnitTest::AllocatorsFixture
{
public:
void SetUp() override;
void TearDown() override;
};
} // namespace AZ::Dom::Tests
@@ -16,131 +16,14 @@
#include <AzCore/Name/NameDictionary.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <Tests/DOM/DomFixtures.h>
namespace Benchmark
namespace AZ::Dom::Benchmark
{
class DomJsonBenchmark : public UnitTest::AllocatorsBenchmarkFixture
class DomJsonBenchmark : public Tests::DomBenchmarkFixture
{
public:
void SetUp(const ::benchmark::State& st) override
{
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);
}
rapidjson::Document GenerateDomJsonBenchmarkDocument(int64_t entryCount, int64_t stringTemplateLength)
{
rapidjson::Document document;
document.SetObject();
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) -> rapidjson::Value
{
buffer = AZStd::string::format("#%i %s", n, entryTemplate.c_str());
return rapidjson::Value(buffer.data(), static_cast<rapidjson::SizeType>(buffer.size()), document.GetAllocator());
};
auto createEntry = [&](int n) -> rapidjson::Value
{
rapidjson::Value entry(rapidjson::kObjectType);
entry.AddMember("string", createString(n), document.GetAllocator());
entry.AddMember("int", rapidjson::Value(n), document.GetAllocator());
entry.AddMember("double", rapidjson::Value(static_cast<double>(n) * 0.5), document.GetAllocator());
entry.AddMember("bool", rapidjson::Value(n % 2 == 0), document.GetAllocator());
entry.AddMember("null", rapidjson::Value(rapidjson::kNullType), document.GetAllocator());
return entry;
};
auto createArray = [&]() -> rapidjson::Value
{
rapidjson::Value array;
array.SetArray();
for (int i = 0; i < entryCount; ++i)
{
array.PushBack(createEntry(i), document.GetAllocator());
}
return array;
};
auto createObject = [&]() -> rapidjson::Value
{
rapidjson::Value object;
object.SetObject();
for (int i = 0; i < entryCount; ++i)
{
buffer = AZStd::string::format("Key%i", i);
rapidjson::Value key;
key.SetString(buffer.data(), static_cast<rapidjson::SizeType>(buffer.length()), document.GetAllocator());
object.AddMember(key.Move(), createArray(), document.GetAllocator());
}
return object;
};
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
#define BENCHMARK_REGISTER_JSON(BaseClass, Method) \
BENCHMARK_REGISTER_F(BaseClass, Method) \
->Args({ 10, 5 }) \
->Args({ 10, 500 }) \
->Args({ 100, 5 }) \
->Args({ 100, 500 }) \
->Unit(benchmark::kMillisecond);
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)(benchmark::State& state)
{
AZ::Dom::JsonBackend backend;
@@ -163,7 +46,7 @@ namespace Benchmark
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, AzDomDeserializeToRapidjsonInPlace)
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)(benchmark::State& state)
{
@@ -187,7 +70,7 @@ namespace Benchmark
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, AzDomDeserializeToAzDomValueInPlace)
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToRapidjson)(benchmark::State& state)
{
@@ -207,7 +90,7 @@ namespace Benchmark
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToRapidjson)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, AzDomDeserializeToRapidjson)
BENCHMARK_DEFINE_F(DomJsonBenchmark, AzDomDeserializeToAzDomValue)(benchmark::State& state)
{
@@ -227,7 +110,7 @@ namespace Benchmark
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, AzDomDeserializeToAzDomValue)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, AzDomDeserializeToAzDomValue)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)(benchmark::State& state)
{
@@ -243,7 +126,7 @@ namespace Benchmark
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, RapidjsonDeserializeToRapidjson)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonMakeComplexObject)(benchmark::State& state)
{
@@ -254,7 +137,7 @@ namespace Benchmark
state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonMakeComplexObject)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, RapidjsonMakeComplexObject)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonLookupMemberByString)(benchmark::State& state)
{
@@ -264,7 +147,9 @@ namespace Benchmark
{
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());
document.AddMember(
rapidjson::Value(key.data(), static_cast<rapidjson::SizeType>(key.size()), document.GetAllocator()), rapidjson::Value(i),
document.GetAllocator());
}
for (auto _ : state)
@@ -293,7 +178,7 @@ namespace Benchmark
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonDeepCopy)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, RapidjsonDeepCopy)
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonCopyAndMutate)(benchmark::State& state)
{
@@ -309,9 +194,8 @@ namespace Benchmark
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonCopyAndMutate)
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomJsonBenchmark, RapidjsonCopyAndMutate)
#undef BENCHMARK_REGISTER_JSON
} // namespace Benchmark
} // namespace AZ::Dom::Benchmark
#endif // defined(HAVE_BENCHMARK)
@@ -13,24 +13,23 @@
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <Tests/DOM/DomFixtures.h>
namespace AZ::Dom::Tests
{
class DomJsonTests : public UnitTest::AllocatorsFixture
class DomJsonTests : public DomTestFixture
{
public:
void SetUp() override
{
UnitTest::AllocatorsFixture::SetUp();
NameDictionary::Create();
DomTestFixture::SetUp();
m_document = AZStd::make_unique<rapidjson::Document>();
}
void TearDown() override
{
m_document.reset();
NameDictionary::Destroy();
UnitTest::AllocatorsFixture::TearDown();
DomTestFixture::TearDown();
}
rapidjson::Value CreateString(const AZStd::string& text)
@@ -6,111 +6,134 @@
*
*/
#include <AzCore/DOM/DomValue.h>
#include <AzCore/DOM/DomUtils.h>
#include <AzCore/DOM/DomValue.h>
#include <AzCore/Name/NameDictionary.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <cinttypes>
#include <Tests/DOM/DomFixtures.h>
namespace AZ::Dom::Benchmark
{
class DomValueBenchmark : public UnitTest::AllocatorsBenchmarkFixture
class DomValueBenchmark : public Tests::DomBenchmarkFixture
{
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, AzDomValueGetType_UsingVariantIndex)(benchmark::State& state)
{
Value intValue(5);
Value boolValue(true);
Value objValue(Type::Object);
Value nodeValue(Type::Node);
Value arrValue(Type::Array);
Value uintValue(5u);
Value doubleValue(4.0);
Value stringValue("foo", true);
for (auto _ : state)
{
(intValue.GetType());
(boolValue.GetType());
(objValue.GetType());
(nodeValue.GetType());
(arrValue.GetType());
(uintValue.GetType());
(doubleValue.GetType());
(stringValue.GetType());
}
state.SetItemsProcessed(8 * state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueGetType_UsingVariantIndex);
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueGetType_UsingVariantVisit)(benchmark::State& state)
{
Value intValue(5);
Value boolValue(true);
Value objValue(Type::Object);
Value nodeValue(Type::Node);
Value arrValue(Type::Array);
Value uintValue(5u);
Value doubleValue(4.0);
Value stringValue("foo", true);
auto getTypeViaVisit = [](const Value& value)
{
return AZStd::visit(
[](auto&& value) constexpr -> Type
{
using CurrentType = AZStd::decay_t<decltype(value)>;
if constexpr (AZStd::is_same_v<CurrentType, AZStd::monostate>)
{
return Type::Null;
}
else if constexpr (AZStd::is_same_v<CurrentType, int64_t>)
{
return Type::Int64;
}
else if constexpr (AZStd::is_same_v<CurrentType, uint64_t>)
{
return Type::Uint64;
}
else if constexpr (AZStd::is_same_v<CurrentType, double>)
{
return Type::Double;
}
else if constexpr (AZStd::is_same_v<CurrentType, bool>)
{
return Type::Bool;
}
else if constexpr (AZStd::is_same_v<CurrentType, AZStd::string_view>)
{
return Type::String;
}
else if constexpr (AZStd::is_same_v<CurrentType, Value::SharedStringType>)
{
return Type::String;
}
else if constexpr (AZStd::is_same_v<CurrentType, Value::ShortStringType>)
{
return Type::String;
}
else if constexpr (AZStd::is_same_v<CurrentType, ObjectPtr>)
{
return Type::Object;
}
else if constexpr (AZStd::is_same_v<CurrentType, ArrayPtr>)
{
return Type::Array;
}
else if constexpr (AZStd::is_same_v<CurrentType, NodePtr>)
{
return Type::Node;
}
else if constexpr (AZStd::is_same_v<CurrentType, Value::OpaqueStorageType>)
{
return Type::Opaque;
}
else
{
AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type");
}
},
value.GetInternalValue());
};
for (auto _ : state)
{
(getTypeViaVisit(intValue));
(getTypeViaVisit(boolValue));
(getTypeViaVisit(objValue));
(getTypeViaVisit(nodeValue));
(getTypeViaVisit(arrValue));
(getTypeViaVisit(uintValue));
(getTypeViaVisit(doubleValue));
(getTypeViaVisit(stringValue));
}
state.SetItemsProcessed(8 * state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueGetType_UsingVariantVisit);
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueMakeComplexObject)(benchmark::State& state)
{
for (auto _ : state)
@@ -120,12 +143,7 @@ namespace AZ::Dom::Benchmark
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);
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomValueBenchmark, AzDomValueMakeComplexObject)
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueShallowCopy)(benchmark::State& state)
{
@@ -139,12 +157,7 @@ namespace AZ::Dom::Benchmark
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueShallowCopy)
->Args({ 10, 5 })
->Args({ 10, 500 })
->Args({ 100, 5 })
->Args({ 100, 500 })
->Unit(benchmark::kNanosecond);
DOM_REGISTER_SERIALIZATION_BENCHMARK_NS(DomValueBenchmark, AzDomValueShallowCopy)
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueCopyAndMutate)(benchmark::State& state)
{
@@ -159,12 +172,7 @@ namespace AZ::Dom::Benchmark
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueCopyAndMutate)
->Args({ 10, 5 })
->Args({ 10, 500 })
->Args({ 100, 5 })
->Args({ 100, 500 })
->Unit(benchmark::kNanosecond);
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomValueBenchmark, AzDomValueCopyAndMutate)
BENCHMARK_DEFINE_F(DomValueBenchmark, AzDomValueDeepCopy)(benchmark::State& state)
{
@@ -178,12 +186,7 @@ namespace AZ::Dom::Benchmark
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_REGISTER_F(DomValueBenchmark, AzDomValueDeepCopy)
->Args({ 10, 5 })
->Args({ 10, 500 })
->Args({ 100, 5 })
->Args({ 100, 500 })
->Unit(benchmark::kMillisecond);
DOM_REGISTER_SERIALIZATION_BENCHMARK_MS(DomValueBenchmark, AzDomValueDeepCopy)
BENCHMARK_DEFINE_F(DomValueBenchmark, LookupMemberByName)(benchmark::State& state)
{
@@ -15,26 +15,18 @@
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/numeric.h>
#include <Tests/DOM/DomFixtures.h>
namespace AZ::Dom::Tests
{
class DomValueTests : public UnitTest::AllocatorsFixture
class DomValueTests : public DomTestFixture
{
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();
DomTestFixture::TearDown();
}
void PerformValueChecks()
@@ -215,6 +215,8 @@ set(FILES
AZStd/Variant.cpp
AZStd/VariantSerialization.cpp
AZStd/VectorAndArray.cpp
DOM/DomFixtures.cpp
DOM/DomFixtures.h
DOM/DomJsonTests.cpp
DOM/DomJsonBenchmarks.cpp
DOM/DomValueTests.cpp