AssetContainer/Reference optim
Bigfoot / Sonarqube (push) Skipped
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 52s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 21s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 30s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 23s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 44s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 32s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 50s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 40s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 52s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 26s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 40s
Bigfoot / Build & Test Debug with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 54s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: game) (push) Successful in 31s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 22s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 23s
Bigfoot / Build & Test RelWithDebInfo with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 43s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: game) (push) Successful in 22s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: ON, Variant: toolmode) (push) Successful in 32s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: game) (push) Successful in 36s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: game) (push) Successful in 34s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: OFF, Variant: toolmode) (push) Successful in 39s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang_asan (Unity Build: ON, Variant: toolmode) (push) Successful in 35s
Bigfoot / Clang Format Checks (push) Failing after 9s
Bigfoot / Build & Test Release with ./ConanProfiles/host/clang (Unity Build: OFF, Variant: toolmode) (push) Successful in 36s

This commit is contained in:
2026-08-25 22:51:38 +02:00
parent be85c650b9
commit 77e26cfa83
4 changed files with 322 additions and 17 deletions
@@ -0,0 +1,164 @@
#include <Engine/Asset/AssetContainer.hpp>
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
#include <effolkronium/random.hpp>
#include <benchmark/benchmark.h>
#include <cstdint>
namespace Bigfoot
{
#ifdef BIGFOOT_TOOLMODE
namespace
{
using Random = effolkronium::random_static;
} // namespace
/****************************************************************************************/
void Insert(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
for (auto _: state)
{
AssetContainer<BenchmarkAsset> container;
for (std::uint32_t i = 0; i < count; ++i)
{
benchmark::DoNotOptimize(container.Insert());
}
benchmark::DoNotOptimize(container);
benchmark::ClobberMemory();
}
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(Insert)->Name("AssetContainer/Insert")->Arg(512)->Arg(8192)->Arg(131072);
/****************************************************************************************/
void Get(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<SlotMapKey<>> keys;
keys.reserve(count);
for (std::uint32_t i = 0; i < count; ++i)
{
keys.push_back(container.Insert());
}
Random::shuffle(keys);
for (auto _: state)
{
std::uint64_t sum = 0;
for (const SlotMapKey<> key: keys)
{
if (const BenchmarkAsset* const asset = container.Get(key))
{
sum += asset->GetHeader().type_id;
}
}
benchmark::DoNotOptimize(sum);
}
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(Get)->Name("AssetContainer/Get")->Arg(512)->Arg(8192)->Arg(131072);
/****************************************************************************************/
void Has(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<UUID> uuids;
uuids.reserve(count);
for (std::uint32_t i = 0; i < count; ++i)
{
const SlotMapKey<> key = container.Insert();
uuids.push_back(container.Get(key)->GetHeader().uuid);
}
Random::shuffle(uuids);
for (auto _: state)
{
bool result = true;
for (const UUID& uuid: uuids)
{
result = result && container.Has(uuid);
}
benchmark::DoNotOptimize(result);
}
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(Has)->Name("AssetContainer/Has")->Arg(512)->Arg(8192)->Arg(131072);
/****************************************************************************************/
void AcquireContainerHandle(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<UUID> uuids;
uuids.reserve(count);
for (std::uint32_t i = 0; i < count; ++i)
{
const SlotMapKey<> key = container.Insert();
uuids.push_back(container.Get(key)->GetHeader().uuid);
}
Random::shuffle(uuids);
for (auto _: state)
{
std::uint64_t sum = 0;
for (const UUID& uuid: uuids)
{
sum += container.AcquireContainerHandle(uuid)->GetHardRefCount();
}
benchmark::DoNotOptimize(sum);
}
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(AcquireContainerHandle)->Name("AssetContainer/AcquireContainerHandle")->Arg(512)->Arg(8192)->Arg(131072);
/****************************************************************************************/
void Remove(benchmark::State& state)
{
const auto count = static_cast<std::uint32_t>(state.range(0));
AssetContainer<BenchmarkAsset> container;
eastl::vector<UUID> uuids;
uuids.reserve(count);
for (auto _: state)
{
state.PauseTiming();
uuids.clear();
for (std::uint32_t i = 0; i < count; ++i)
{
const SlotMapKey<> key = container.Insert();
uuids.push_back(container.Get(key)->GetHeader().uuid);
}
Random::shuffle(uuids);
state.ResumeTiming();
for (const UUID& uuid: uuids)
{
container.Remove(uuid);
}
}
state.SetItemsProcessed(state.iterations() * count);
}
BENCHMARK(Remove)->Name("AssetContainer/Remove")->Arg(512)->Arg(8192)->Arg(131072);
#endif
} // namespace Bigfoot
@@ -0,0 +1,128 @@
#include <Engine/Asset/AssetLibrary.hpp>
#include <Engine/Asset/Reference.hpp>
#include <AssetsForBenchmarking/Asset/BenchmarkAsset.hpp>
#include <EASTL/vector.h>
#include <benchmark/benchmark.h>
#include <cstddef>
#include <utility>
namespace Bigfoot
{
#ifdef BIGFOOT_TOOLMODE
template<template<class> class REFERENCE>
void Construct(benchmark::State& state)
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
for (auto _: state)
{
REFERENCE<BenchmarkAsset> reference {uuid};
benchmark::DoNotOptimize(reference);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_TEMPLATE(Construct, HardReference)->Name("Reference/HardReference/Construct");
BENCHMARK_TEMPLATE(Construct, SoftReference)->Name("Reference/SoftReference/Construct");
/****************************************************************************************/
template<template<class> class REFERENCE>
void Get(benchmark::State& state)
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
REFERENCE<BenchmarkAsset> reference {uuid};
for (auto _: state)
{
benchmark::DoNotOptimize(reference.Get());
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_TEMPLATE(Get, HardReference)->Name("Reference/HardReference/Get");
BENCHMARK_TEMPLATE(Get, SoftReference)->Name("Reference/SoftReference/Get");
/****************************************************************************************/
template<template<class> class REFERENCE>
void Copy(benchmark::State& state)
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
REFERENCE<BenchmarkAsset> reference {uuid};
for (auto _: state)
{
REFERENCE<BenchmarkAsset> copy {reference};
benchmark::DoNotOptimize(copy);
}
state.SetItemsProcessed(state.iterations());
}
BENCHMARK_TEMPLATE(Copy, HardReference)->Name("Reference/HardReference/Copy");
BENCHMARK_TEMPLATE(Copy, SoftReference)->Name("Reference/SoftReference/Copy");
/****************************************************************************************/
template<template<class> class REFERENCE>
void Move(benchmark::State& state)
{
AssetLibrary library;
AssetContainer<BenchmarkAsset> container;
std::ignore = library.Insert(&container);
ScopedActiveAssetLibrary scopedLibrary {&library};
const SlotMapKey<> key = container.Insert();
const UUID uuid = container.Get(key)->GetHeader().uuid;
constexpr std::size_t BATCH_SIZE = 128;
eastl::vector<REFERENCE<BenchmarkAsset>> batch;
batch.reserve(BATCH_SIZE);
for (auto _: state)
{
state.PauseTiming();
batch.clear();
for (std::size_t i = 0; i < BATCH_SIZE; ++i)
{
batch.emplace_back(uuid);
}
state.ResumeTiming();
for (REFERENCE<BenchmarkAsset>& reference: batch)
{
REFERENCE<BenchmarkAsset> moved {std::move(reference)};
benchmark::DoNotOptimize(moved);
}
}
state.SetItemsProcessed(state.iterations() * BATCH_SIZE);
}
BENCHMARK_TEMPLATE(Move, HardReference)->Name("Reference/HardReference/Move");
BENCHMARK_TEMPLATE(Move, SoftReference)->Name("Reference/SoftReference/Move");
#endif
} // namespace Bigfoot
@@ -164,7 +164,7 @@ class AssetContainerBase
[[nodiscard]]
ContainerHandle* AcquireContainerHandle(const UUID& p_uuid)
{
return m_handles.Get(GetOrCreateEntry(p_uuid).m_handleSlotKey);
return GetOrCreateEntry(p_uuid).m_handle;
}
/*
@@ -174,7 +174,7 @@ class AssetContainerBase
*/
virtual void Remove(const UUID& p_uuid)
{
if (Entry* entry = FindEntry(p_uuid); entry != nullptr && entry->m_handleSlotKey.Valid())
if (Entry* entry = FindEntry(p_uuid); entry != nullptr)
{
m_handles.Remove(entry->m_handleSlotKey);
m_uuidToEntry.erase(p_uuid);
@@ -195,6 +195,11 @@ class AssetContainerBase
* SlotMapKey to the asset
*/
SlotMapKey m_assetSlotKey;
/*
* The handle itself, cached to avoid a SlotMap lookup on every access
*/
ContainerHandle* m_handle = nullptr;
};
AssetContainerBase() = default;
@@ -241,23 +246,12 @@ class AssetContainerBase
if (inserted)
{
it->second.m_handleSlotKey = m_handles.Insert();
it->second.m_handle = m_handles.Get(it->second.m_handleSlotKey);
}
return it->second;
}
/*
* Get a ContainerHandle
*
* \param p_handleSlotKey The key to the container
* \return The container
*/
[[nodiscard]]
ContainerHandle& FindContainerHandle(const SlotMapKey p_handleSlotKey)
{
return *m_handles.Get(p_handleSlotKey);
}
private:
/*
* HandleContainers
@@ -267,7 +261,7 @@ class AssetContainerBase
/*
* Every Asset added to the map
*/
ankerl::unordered_dense::segmented_map<UUID, Entry> m_uuidToEntry;
ankerl::unordered_dense::map<UUID, Entry> m_uuidToEntry;
};
/****************************************************************************************/
@@ -303,7 +297,7 @@ class AssetContainer: public AssetContainerBase
else
{
entry.m_assetSlotKey = slotKey;
FindContainerHandle(entry.m_handleSlotKey).SetAsset(asset);
entry.m_handle->SetAsset(asset);
}
return slotKey;
@@ -342,7 +336,7 @@ class AssetContainer: public AssetContainerBase
{
if (Entry* entry = FindEntry(p_uuid); entry != nullptr && entry->m_assetSlotKey.Valid())
{
FindContainerHandle(entry->m_handleSlotKey).SetAsset(nullptr);
entry->m_handle->SetAsset(nullptr);
m_assets.Remove(entry->m_assetSlotKey);
entry->m_assetSlotKey = SlotMapKey {};
}
@@ -9,6 +9,8 @@
#include <fmt/format.h>
#include <uuid.h>
#include <ankerl/unordered_dense.h>
namespace Bigfoot
{
class UUID
@@ -112,6 +114,23 @@ struct std::hash<Bigfoot::UUID>
}
};
/****************************************************************************************/
template<>
struct ankerl::unordered_dense::hash<Bigfoot::UUID>
{
using is_avalanching = void;
[[nodiscard]]
std::uint64_t operator()(const Bigfoot::UUID& p_uuid) const noexcept
{
const auto bytes = p_uuid.ToBytes();
return detail::wyhash::hash(bytes.data(), bytes.size());
}
};
/****************************************************************************************/
template<>
struct fmt::formatter<Bigfoot::UUID>
{