Add benchmarks, some light optimizations like SSO
Signed-off-by: Nicholas Van Sickle <nvsickle@amazon.com>
This commit is contained in:
@@ -21,4 +21,16 @@ namespace AZ::Dom::Utils
|
||||
{
|
||||
return backend.ReadFromBufferInPlace(string.data(), string.size(), visitor);
|
||||
}
|
||||
|
||||
AZ::Outcome<Value, AZStd::string> AZ::Dom::Utils::WriteToValue(Backend::WriteCallback writeCallback)
|
||||
{
|
||||
Value value;
|
||||
AZStd::unique_ptr<Visitor> writer = value.GetWriteHandler();
|
||||
Visitor::Result result = writeCallback(*writer);
|
||||
if (!result.IsSuccess())
|
||||
{
|
||||
return AZ::Failure(result.GetError().FormatVisitorErrorMessage());
|
||||
}
|
||||
return AZ::Success(AZStd::move(value));
|
||||
}
|
||||
} // namespace AZ::Dom::Utils
|
||||
|
||||
@@ -9,9 +9,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/DOM/DomBackend.h>
|
||||
#include <AzCore/DOM/DomValue.h>
|
||||
|
||||
namespace AZ::Dom::Utils
|
||||
{
|
||||
Visitor::Result ReadFromString(Backend& backend, AZStd::string_view string, AZ::Dom::Lifetime lifetime, Visitor& visitor);
|
||||
Visitor::Result ReadFromStringInPlace(Backend& backend, AZStd::string& string, Visitor& visitor);
|
||||
|
||||
AZ::Outcome<Value, AZStd::string> WriteToValue(Backend::WriteCallback writeCallback);
|
||||
} // namespace AZ::Dom::Utils
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace AZ::Dom
|
||||
{
|
||||
if (refCountedPointer.use_count() > 1)
|
||||
{
|
||||
AZStd::shared_ptr<T> newPointer = AZStd::make_shared<T>();
|
||||
AZStd::shared_ptr<T> newPointer = AZStd::allocate_shared<T>(AZStdAlloc<ValueAllocator>());
|
||||
*newPointer = *refCountedPointer;
|
||||
refCountedPointer = AZStd::move(newPointer);
|
||||
}
|
||||
@@ -71,8 +71,8 @@ namespace AZ::Dom
|
||||
}
|
||||
|
||||
Value::Value(Value&& value) noexcept
|
||||
: m_value(value.m_value)
|
||||
{
|
||||
operator=(value);
|
||||
}
|
||||
|
||||
Value::Value(AZStd::string_view string, bool copy)
|
||||
@@ -174,7 +174,7 @@ namespace AZ::Dom
|
||||
|
||||
Value& Value::operator=(Value&& other) noexcept
|
||||
{
|
||||
m_value = other.m_value;
|
||||
m_value.swap(other.m_value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ namespace AZ::Dom
|
||||
|
||||
void Value::Swap(Value& other) noexcept
|
||||
{
|
||||
AZStd::swap(m_value, other.m_value);
|
||||
m_value.swap(other.m_value);
|
||||
}
|
||||
|
||||
Type Dom::Value::GetType() const
|
||||
@@ -229,14 +229,15 @@ namespace AZ::Dom
|
||||
return AZStd::get<bool>(m_value) ? Type::TrueType : Type::FalseType;
|
||||
case 5: // AZStd::string_view
|
||||
case 6: // AZStd::shared_ptr<AZStd::string>
|
||||
case 7: // ShortStringType
|
||||
return Type::StringType;
|
||||
case 7: // ObjectPtr
|
||||
case 8: // ObjectPtr
|
||||
return Type::ObjectType;
|
||||
case 8: // ArrayPtr
|
||||
case 9: // ArrayPtr
|
||||
return Type::ArrayType;
|
||||
case 9: // NodePtr
|
||||
case 10: // NodePtr
|
||||
return Type::NodeType;
|
||||
case 10: // AZStd::any*
|
||||
case 11: // AZStd::any*
|
||||
return Type::OpaqueType;
|
||||
}
|
||||
AZ_Assert(false, "AZ::Dom::Value::GetType: m_value has an unexpected type");
|
||||
@@ -310,7 +311,7 @@ namespace AZ::Dom
|
||||
|
||||
Value& Value::SetObject()
|
||||
{
|
||||
m_value = AZStd::make_shared<Object>();
|
||||
m_value = AZStd::allocate_shared<Object>(AZStdAlloc<ValueAllocator>());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -511,6 +512,7 @@ namespace AZ::Dom
|
||||
Value& Value::AddMember(KeyType name, const Value& value)
|
||||
{
|
||||
Object::ContainerType& object = GetObjectInternal();
|
||||
object.reserve((object.size() / Object::ReserveIncrement + 1) * Object::ReserveIncrement);
|
||||
if (auto memberIt = FindMutableMember(name); memberIt != object.end())
|
||||
{
|
||||
memberIt->second = value;
|
||||
@@ -613,7 +615,7 @@ namespace AZ::Dom
|
||||
|
||||
Value& Value::SetArray()
|
||||
{
|
||||
m_value = AZStd::make_shared<Array>();
|
||||
m_value = AZStd::allocate_shared<Array>(AZStdAlloc<ValueAllocator>());
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -685,7 +687,9 @@ namespace AZ::Dom
|
||||
|
||||
Value& Value::PushBack(Value value)
|
||||
{
|
||||
GetArrayInternal().push_back(AZStd::move(value));
|
||||
Array::ContainerType& array = GetArrayInternal();
|
||||
array.reserve((array.size() / Array::ReserveIncrement + 1) * Array::ReserveIncrement);
|
||||
array.push_back(AZStd::move(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -717,7 +721,7 @@ namespace AZ::Dom
|
||||
|
||||
void Value::SetNode(AZ::Name name)
|
||||
{
|
||||
m_value = AZStd::make_shared<Node>(name);
|
||||
m_value = AZStd::allocate_shared<Node>(AZStdAlloc<ValueAllocator>(), name);
|
||||
}
|
||||
|
||||
void Value::SetNode(AZStd::string_view name)
|
||||
@@ -899,6 +903,11 @@ namespace AZ::Dom
|
||||
return AZStd::get<AZStd::string_view>(m_value);
|
||||
case 6: // AZStd::shared_ptr<AZStd::string>
|
||||
return *AZStd::get<AZStd::shared_ptr<AZStd::string>>(m_value);
|
||||
case 7: // ShortStringType
|
||||
{
|
||||
const ShortStringType& ShortString = AZStd::get<ShortStringType>(m_value);
|
||||
return { ShortString.m_data.data(), ShortString.m_size };
|
||||
}
|
||||
}
|
||||
AZ_Assert(false, "AZ::Dom::Value: Called GetString on a non-string type");
|
||||
return {};
|
||||
@@ -911,12 +920,26 @@ namespace AZ::Dom
|
||||
|
||||
void Value::SetString(AZStd::string_view value)
|
||||
{
|
||||
if (value.size() <= ShortStringSize)
|
||||
{
|
||||
ShortStringType buffer;
|
||||
buffer.m_size = value.size();
|
||||
memcpy(buffer.m_data.data(), value.data(), buffer.m_size);
|
||||
m_value = buffer;
|
||||
}
|
||||
m_value = value;
|
||||
}
|
||||
|
||||
void Value::CopyFromString(AZStd::string_view value)
|
||||
{
|
||||
m_value = AZStd::make_shared<AZStd::string>(value);
|
||||
if (value.size() <= ShortStringSize)
|
||||
{
|
||||
SetString(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_value = AZStd::allocate_shared<AZStd::string>(AZStdAlloc<ValueAllocator>(), value);
|
||||
}
|
||||
}
|
||||
|
||||
AZStd::any& Value::GetOpaqueValue() const
|
||||
|
||||
@@ -11,11 +11,13 @@
|
||||
#include <AzCore/DOM/DomBackend.h>
|
||||
#include <AzCore/DOM/DomVisitor.h>
|
||||
#include <AzCore/Memory/Memory.h>
|
||||
#include <AzCore/Memory/PoolAllocator.h>
|
||||
#include <AzCore/std/containers/stack.h>
|
||||
#include <AzCore/std/containers/unordered_map.h>
|
||||
#include <AzCore/std/containers/variant.h>
|
||||
#include <AzCore/std/containers/vector.h>
|
||||
#include <AzCore/std/smart_ptr/shared_ptr.h>
|
||||
#include <AzCore/std/containers/array.h>
|
||||
|
||||
namespace AZ::Dom
|
||||
{
|
||||
@@ -34,6 +36,22 @@ namespace AZ::Dom
|
||||
OpaqueType = 8,
|
||||
};
|
||||
|
||||
class ValueAllocator final : public ThreadPoolBase<ValueAllocator, false>
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ValueAllocator, SystemAllocator, 0);
|
||||
AZ_TYPE_INFO(ValueAllocator, "{5BC8B389-72C7-459E-B502-12E74D61869F}");
|
||||
|
||||
ValueAllocator()
|
||||
: ThreadPoolBase<ValueAllocator, false>("DomValueAllocator", "Allocator for AZ::Dom::Value")
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// class ValueAllocator : public Internal::PoolAllocatorHelper<PoolScema
|
||||
|
||||
// using ValueAllocator = ThreadPoolAllocator;
|
||||
|
||||
class Value;
|
||||
|
||||
class Array
|
||||
@@ -42,6 +60,7 @@ namespace AZ::Dom
|
||||
using ContainerType = AZStd::vector<Value>;
|
||||
using Iterator = ContainerType::iterator;
|
||||
using ConstIterator = ContainerType::const_iterator;
|
||||
static constexpr const size_t ReserveIncrement = 4;
|
||||
|
||||
private:
|
||||
ContainerType m_values;
|
||||
@@ -59,6 +78,7 @@ namespace AZ::Dom
|
||||
using ContainerType = AZStd::vector<EntryType>;
|
||||
using Iterator = ContainerType::iterator;
|
||||
using ConstIterator = ContainerType::const_iterator;
|
||||
static constexpr const size_t ReserveIncrement = 8;
|
||||
|
||||
private:
|
||||
ContainerType m_values;
|
||||
@@ -293,6 +313,18 @@ namespace AZ::Dom
|
||||
|
||||
explicit Value(AZStd::any* opaqueValue);
|
||||
|
||||
static constexpr const size_t ShortStringSize = sizeof(AZStd::string_view) - sizeof(size_t);
|
||||
struct ShortStringType
|
||||
{
|
||||
AZStd::array<char, ShortStringSize> m_data;
|
||||
size_t m_size;
|
||||
|
||||
bool operator==(const ShortStringType& other) const
|
||||
{
|
||||
return m_size == other.m_size ? memcmp(m_data.data(), other.m_data.data(), m_size) == 0 : false;
|
||||
}
|
||||
};
|
||||
|
||||
// If using the the copy on write model, anything stored internally as a shared_ptr will
|
||||
// detach and copy when doing a mutating operation if use_count() > 1.
|
||||
|
||||
@@ -311,6 +343,7 @@ namespace AZ::Dom
|
||||
// StringType
|
||||
AZStd::string_view,
|
||||
AZStd::shared_ptr<AZStd::string>,
|
||||
ShortStringType,
|
||||
// ObjectType
|
||||
ObjectPtr,
|
||||
// ArrayType
|
||||
@@ -320,6 +353,10 @@ namespace AZ::Dom
|
||||
// OpaqueType
|
||||
AZStd::any*>;
|
||||
|
||||
static_assert(
|
||||
sizeof(ValueType) == sizeof(AZStd::variant<ShortStringType>),
|
||||
"ValueType should have no members larger than ShortStringType");
|
||||
|
||||
ValueType m_value;
|
||||
};
|
||||
} // namespace AZ::Dom
|
||||
|
||||
@@ -103,35 +103,68 @@ namespace AZ::Dom
|
||||
}
|
||||
|
||||
const ValueInfo& topEntry = m_entryStack.top();
|
||||
if (topEntry.m_container.GetType() != containerType)
|
||||
Value& container = topEntry.m_container;
|
||||
ValueBuffer& buffer = GetValueBuffer();
|
||||
|
||||
if (container.GetType() != containerType)
|
||||
{
|
||||
return VisitorFailure(
|
||||
VisitorErrorCode::InternalError,
|
||||
AZStd::string::format("AZ::Dom::ValueWriter: %s called from within a different container type", endMethodName));
|
||||
}
|
||||
|
||||
if (topEntry.m_attributeCount != attributeCount)
|
||||
if (buffer.m_attributes.size() != attributeCount)
|
||||
{
|
||||
return VisitorFailure(
|
||||
VisitorErrorCode::InternalError,
|
||||
AZStd::string::format(
|
||||
"AZ::Dom::ValueWriter: %s expected %llu attributes but received %llu attributes instead", endMethodName, attributeCount,
|
||||
topEntry.m_attributeCount));
|
||||
buffer.m_attributes.size()));
|
||||
}
|
||||
|
||||
if (topEntry.m_elementCount != elementCount)
|
||||
if (buffer.m_elements.size() != elementCount)
|
||||
{
|
||||
return VisitorFailure(
|
||||
VisitorErrorCode::InternalError,
|
||||
AZStd::string::format(
|
||||
"AZ::Dom::ValueWriter: %s expected %llu elements but received %llu elements instead", endMethodName, elementCount,
|
||||
topEntry.m_elementCount));
|
||||
buffer.m_elements.size()));
|
||||
}
|
||||
if (buffer.m_attributes.size() > 0)
|
||||
{
|
||||
container.MemberReserve(buffer.m_attributes.size());
|
||||
for (AZStd::pair<AZ::Name, Value>& entry : buffer.m_attributes)
|
||||
{
|
||||
container.AddMember(AZStd::move(entry.first), AZStd::move(entry.second));
|
||||
}
|
||||
buffer.m_attributes.clear();
|
||||
}
|
||||
|
||||
if(buffer.m_elements.size() > 0)
|
||||
{
|
||||
container.Reserve(buffer.m_elements.size());
|
||||
for (Value& entry : buffer.m_elements)
|
||||
{
|
||||
container.PushBack(AZStd::move(entry));
|
||||
}
|
||||
buffer.m_elements.clear();
|
||||
}
|
||||
|
||||
m_entryStack.pop();
|
||||
return FinishWrite();
|
||||
}
|
||||
|
||||
ValueWriter::ValueBuffer& ValueWriter::GetValueBuffer()
|
||||
{
|
||||
if (m_entryStack.size() <= m_valueBuffers.size())
|
||||
{
|
||||
return m_valueBuffers[m_entryStack.size() - 1];
|
||||
}
|
||||
|
||||
m_valueBuffers.resize(m_entryStack.size());
|
||||
return m_valueBuffers[m_entryStack.size() - 1];
|
||||
}
|
||||
|
||||
Visitor::Result ValueWriter::EndObject(AZ::u64 attributeCount)
|
||||
{
|
||||
return EndContainer(Type::ObjectType, attributeCount, 0);
|
||||
@@ -192,16 +225,16 @@ namespace AZ::Dom
|
||||
m_entryStack.top().m_value.Swap(value);
|
||||
ValueInfo& newEntry = m_entryStack.top();
|
||||
|
||||
constexpr const size_t reserveSize = 8;
|
||||
|
||||
if (!newEntry.m_key.IsEmpty())
|
||||
{
|
||||
newEntry.m_container.AddMember(newEntry.m_key, AZStd::move(value));
|
||||
GetValueBuffer().m_attributes.emplace_back(AZStd::move(newEntry.m_key), AZStd::move(value));
|
||||
newEntry.m_key = AZ::Name();
|
||||
++newEntry.m_attributeCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
newEntry.m_container.PushBack(AZStd::move(value));
|
||||
++newEntry.m_elementCount;
|
||||
GetValueBuffer().m_elements.emplace_back(AZStd::move(value));
|
||||
}
|
||||
|
||||
return VisitorSuccess();
|
||||
|
||||
@@ -48,11 +48,18 @@ namespace AZ::Dom
|
||||
KeyType m_key;
|
||||
Value m_value;
|
||||
Value& m_container;
|
||||
AZ::u64 m_attributeCount = 0;
|
||||
AZ::u64 m_elementCount = 0;
|
||||
};
|
||||
|
||||
struct ValueBuffer
|
||||
{
|
||||
AZStd::vector<Value> m_elements;
|
||||
AZStd::vector<AZStd::pair<AZ::Name, Value>> m_attributes;
|
||||
};
|
||||
|
||||
ValueBuffer& GetValueBuffer();
|
||||
|
||||
Value& m_result;
|
||||
AZStd::stack<ValueInfo> m_entryStack;
|
||||
AZStd::vector<ValueBuffer> m_valueBuffers;
|
||||
};
|
||||
} // namespace AZ::Dom
|
||||
|
||||
@@ -25,12 +25,12 @@ namespace AZ
|
||||
* Template you can use to create your own thread pool allocators, as you can't inherit from ThreadPoolAllocator.
|
||||
* This is the case because we use tread local storage and we need separate "static" instance for each allocator.
|
||||
*/
|
||||
template<class Schema>
|
||||
template<class Schema, bool ProfileAllocations>
|
||||
class PoolAllocatorHelper
|
||||
: public SimpleSchemaAllocator<Schema, typename Schema::Descriptor, /* ProfileAllocations */ true, /* ReportOutOfMemory */ false>
|
||||
: public SimpleSchemaAllocator<Schema, typename Schema::Descriptor, ProfileAllocations, /* ReportOutOfMemory */ false>
|
||||
{
|
||||
public:
|
||||
using Base = SimpleSchemaAllocator<Schema, typename Schema::Descriptor, true, false>;
|
||||
using Base = SimpleSchemaAllocator<Schema, typename Schema::Descriptor, ProfileAllocations, false>;
|
||||
using pointer_type = typename Base::pointer_type;
|
||||
using size_type = typename Base::size_type;
|
||||
using difference_type = typename Base::difference_type;
|
||||
@@ -140,13 +140,13 @@ namespace AZ
|
||||
* use PoolAllocatorThreadSafe or do the sync yourself.
|
||||
*/
|
||||
class PoolAllocator
|
||||
: public Internal::PoolAllocatorHelper<PoolSchema>
|
||||
: public Internal::PoolAllocatorHelper<PoolSchema, /*ProfileAllocations*/ true>
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(PoolAllocator, SystemAllocator, 0);
|
||||
AZ_TYPE_INFO(PoolAllocator, "{D3DC61AF-0949-4BFA-87E0-62FA03A4C025}");
|
||||
|
||||
using Base = Internal::PoolAllocatorHelper<PoolSchema>;
|
||||
using Base = Internal::PoolAllocatorHelper<PoolSchema, true>;
|
||||
|
||||
PoolAllocator(const char* name = "PoolAllocator", const char* desc = "Generic pool allocator for small objects")
|
||||
: Base(name, desc)
|
||||
@@ -154,21 +154,21 @@ namespace AZ
|
||||
}
|
||||
};
|
||||
|
||||
template<class Allocator>
|
||||
using ThreadPoolBase = Internal::PoolAllocatorHelper<ThreadPoolSchemaHelper<Allocator> >;
|
||||
template<class Allocator, bool ProfileAllocations = true>
|
||||
using ThreadPoolBase = Internal::PoolAllocatorHelper<ThreadPoolSchemaHelper<Allocator>, ProfileAllocations >;
|
||||
|
||||
/*!
|
||||
* Thread safe pool allocator. If you want to create your own thread pool heap,
|
||||
* inherit from ThreadPoolBase, as we need unique static variable for allocator type.
|
||||
*/
|
||||
class ThreadPoolAllocator final
|
||||
: public ThreadPoolBase<ThreadPoolAllocator>
|
||||
: public ThreadPoolBase<ThreadPoolAllocator, /*ProfileAllocations*/ true>
|
||||
{
|
||||
public:
|
||||
AZ_CLASS_ALLOCATOR(ThreadPoolAllocator, SystemAllocator, 0);
|
||||
AZ_TYPE_INFO(ThreadPoolAllocator, "{05B4857F-CD06-4942-99FD-CA6A7BAE855A}");
|
||||
|
||||
using Base = ThreadPoolBase<ThreadPoolAllocator>;
|
||||
using Base = ThreadPoolBase<ThreadPoolAllocator, true>;
|
||||
|
||||
ThreadPoolAllocator()
|
||||
: Base("PoolAllocatorThreadSafe", "Generic thread safe pool allocator for small objects")
|
||||
|
||||
@@ -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,22 +26,26 @@ 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);
|
||||
}
|
||||
@@ -143,6 +148,30 @@ namespace Benchmark
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDocumentInPlace)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDomValueInPlace)(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);
|
||||
});
|
||||
|
||||
benchmark::DoNotOptimize(result.GetValue());
|
||||
}
|
||||
|
||||
state.SetBytesProcessed(serializedPayload.size() * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, DomDeserializeToDomValueInPlace)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, DomDeserializeToDocument)(benchmark::State& state)
|
||||
{
|
||||
AZ::Dom::JsonBackend backend;
|
||||
@@ -179,6 +208,17 @@ namespace Benchmark
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, JsonUtilsDeserializeToDocument)
|
||||
|
||||
BENCHMARK_DEFINE_F(DomJsonBenchmark, RapidjsonPayloadGeneration)(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state)
|
||||
{
|
||||
benchmark::DoNotOptimize(GenerateDomJsonBenchmarkPayload(state.range(0), state.range(1)));
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_JSON(DomJsonBenchmark, RapidjsonPayloadGeneration)
|
||||
|
||||
#undef BENCHMARK_REGISTER_JSON
|
||||
} // namespace Benchmark
|
||||
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* 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/UnitTest/TestTypes.h>
|
||||
|
||||
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::ObjectType);
|
||||
|
||||
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::ObjectType);
|
||||
entry.AddMember("string", createString(n));
|
||||
entry.AddMember("int", n);
|
||||
entry.AddMember("double", static_cast<double>(n) * 0.5);
|
||||
entry.AddMember("bool", n % 2 == 0);
|
||||
entry.AddMember("null", Value(Type::NullType));
|
||||
return entry;
|
||||
};
|
||||
|
||||
auto createArray = [&]() -> Value
|
||||
{
|
||||
Value array(Type::ArrayType);
|
||||
for (int i = 0; i < entryCount; ++i)
|
||||
{
|
||||
array.PushBack(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;
|
||||
}
|
||||
};
|
||||
|
||||
BENCHMARK_DEFINE_F(DomValueBenchmark, ValuePayloadGeneration)(benchmark::State& state)
|
||||
{
|
||||
for (auto _ : state)
|
||||
{
|
||||
benchmark::DoNotOptimize(GenerateDomBenchmarkPayload(state.range(0), state.range(1)));
|
||||
}
|
||||
|
||||
state.SetItemsProcessed(state.range(0) * state.range(0) * state.iterations());
|
||||
}
|
||||
BENCHMARK_REGISTER_F(DomValueBenchmark, ValuePayloadGeneration)
|
||||
->Args({ 10, 5 })
|
||||
->Args({ 10, 500 })
|
||||
->Args({ 100, 5 })
|
||||
->Args({ 100, 500 })
|
||||
->Unit(benchmark::kMillisecond);
|
||||
} // namespace AZ::Dom::Benchmark
|
||||
|
||||
@@ -25,12 +25,14 @@ namespace AZ::Dom::Tests
|
||||
{
|
||||
UnitTest::AllocatorsFixture::SetUp();
|
||||
NameDictionary::Create();
|
||||
AZ::AllocatorInstance<ValueAllocator>::Create();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
{
|
||||
m_value = Value();
|
||||
|
||||
AZ::AllocatorInstance<ValueAllocator>::Destroy();
|
||||
NameDictionary::Destroy();
|
||||
UnitTest::AllocatorsFixture::TearDown();
|
||||
}
|
||||
@@ -279,19 +281,24 @@ namespace AZ::Dom::Tests
|
||||
|
||||
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 = "foo";
|
||||
AZStd::string stringToReference = s1;
|
||||
m_value["no_copy"] = Value(stringToReference, false);
|
||||
AZStd::string stringToCopy = "bar";
|
||||
AZStd::string stringToCopy = s2;
|
||||
m_value["copy"] = Value(stringToCopy, true);
|
||||
|
||||
EXPECT_EQ(m_value["no_copy"].GetType(), Type::StringType);
|
||||
EXPECT_EQ(m_value["no_copy"].GetString(), stringToReference);
|
||||
EXPECT_EQ(m_value["no_copy"].GetString().data(), stringToReference.data());
|
||||
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::StringType);
|
||||
EXPECT_EQ(m_value["copy"].GetString(), stringToCopy);
|
||||
EXPECT_NE(m_value["copy"].GetString().data(), stringToCopy.data());
|
||||
EXPECT_EQ(m_value["copy"].GetString(), s2);
|
||||
stringToCopy.at(0) = 'F';
|
||||
EXPECT_EQ(m_value["copy"].GetString(), s2);
|
||||
|
||||
PerformValueChecks();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user