diff --git a/Bigfoot/Benchmarks/Engine/BigFile/BigFile.cpp b/Bigfoot/Benchmarks/Engine/BigFile/BigFile.cpp index 4ab3ef3..80db522 100644 --- a/Bigfoot/Benchmarks/Engine/BigFile/BigFile.cpp +++ b/Bigfoot/Benchmarks/Engine/BigFile/BigFile.cpp @@ -116,7 +116,7 @@ BENCHMARK_DEFINE_F(BigFileFixture, GetAssetFlatbuffer)(benchmark::State& state) state.SetBytesProcessed(bytesProcessed); } -BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffer)->Name("BigFile/GetAssetFlatbuffer"); +BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffer)->Name("BigFile/GetAssetFlatbuffer")->UseRealTime(); /****************************************************************************************/ @@ -153,7 +153,8 @@ BENCHMARK_REGISTER_F(BigFileFixture, GetAssetFlatbuffers) ->Arg(8) ->Arg(64) ->Arg(512) - ->Arg(4096); + ->Arg(4096) + ->UseRealTime(); /****************************************************************************************/ @@ -168,7 +169,7 @@ BENCHMARK_DEFINE_F(BigFileFixture, GetAssetHardReferences)(benchmark::State& sta state.SetItemsProcessed(state.iterations()); } -BENCHMARK_REGISTER_F(BigFileFixture, GetAssetHardReferences)->Name("BigFile/GetAssetHardReferences"); +BENCHMARK_REGISTER_F(BigFileFixture, GetAssetHardReferences)->Name("BigFile/GetAssetHardReferences")->UseRealTime(); /****************************************************************************************/ @@ -195,7 +196,8 @@ BENCHMARK_REGISTER_F(BigFileFixture, GetAssetsHardReferences) ->Arg(8) ->Arg(64) ->Arg(512) - ->Arg(4096); + ->Arg(4096) + ->UseRealTime(); /****************************************************************************************/ @@ -222,7 +224,7 @@ BENCHMARK_DEFINE_F(BigFileFixture, SaveAssetFlatbuffer)(benchmark::State& state) state.SetBytesProcessed(bytesProcessed); } -BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffer)->Name("BigFile/SaveAssetFlatbuffer"); +BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffer)->Name("BigFile/SaveAssetFlatbuffer")->UseRealTime(); /****************************************************************************************/ @@ -265,6 +267,7 @@ BENCHMARK_REGISTER_F(BigFileFixture, SaveAssetFlatbuffers) ->Arg(8) ->Arg(64) ->Arg(512) - ->Arg(4096); + ->Arg(4096) + ->UseRealTime(); #endif } // namespace Bigfoot diff --git a/Bigfoot/Benchmarks/Engine/Sasquatch/Sasquatch.cpp b/Bigfoot/Benchmarks/Engine/Sasquatch/Sasquatch.cpp index 9255b31..994af0f 100644 --- a/Bigfoot/Benchmarks/Engine/Sasquatch/Sasquatch.cpp +++ b/Bigfoot/Benchmarks/Engine/Sasquatch/Sasquatch.cpp @@ -151,7 +151,7 @@ BENCHMARK_DEFINE_F(SasquatchFixture, GetAssetHardReferences)(benchmark::State& s state.SetItemsProcessed(state.iterations()); } -BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetHardReferences)->Name("Sasquatch/GetAssetHardReferences"); +BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetHardReferences)->Name("Sasquatch/GetAssetHardReferences")->UseRealTime(); /****************************************************************************************/ @@ -178,7 +178,8 @@ BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetsHardReferences) ->Arg(8) ->Arg(64) ->Arg(512) - ->Arg(4096); + ->Arg(4096) + ->UseRealTime(); /****************************************************************************************/ @@ -199,7 +200,7 @@ BENCHMARK_DEFINE_F(SasquatchFixture, GetAssetFlatbuffer)(benchmark::State& state state.SetBytesProcessed(bytesProcessed); } -BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetFlatbuffer)->Name("Sasquatch/GetAssetFlatbuffer"); +BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetFlatbuffer)->Name("Sasquatch/GetAssetFlatbuffer")->UseRealTime(); /****************************************************************************************/ @@ -236,6 +237,7 @@ BENCHMARK_REGISTER_F(SasquatchFixture, GetAssetFlatbuffers) ->Arg(8) ->Arg(64) ->Arg(512) - ->Arg(4096); + ->Arg(4096) + ->UseRealTime(); #endif } // namespace Bigfoot diff --git a/Bigfoot/Benchmarks/Engine/SerializationManager/SerializationManager.cpp b/Bigfoot/Benchmarks/Engine/SerializationManager/SerializationManager.cpp new file mode 100644 index 0000000..1509e70 --- /dev/null +++ b/Bigfoot/Benchmarks/Engine/SerializationManager/SerializationManager.cpp @@ -0,0 +1,361 @@ +/********************************************************************* + * \file SerializationManager.cpp + * + * \author Romain BOULLARD + * \date August 2026 + *********************************************************************/ +#include + +#include +#include + +#if defined BIGFOOT_TOOLMODE +#include + +#include +#elif defined BIGFOOT_GAME +#include + +#include +#endif + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace Bigfoot +{ +using Random = effolkronium::random_static; + +class SerializationManagerFixture: public benchmark::Fixture +{ + protected: + static constexpr std::uint32_t ROOT_ASSET_COUNT = 20'000; + static constexpr std::uint32_t REFS_PER_ROOT = 4; + static constexpr std::size_t ASSET_PAYLOAD_SIZE = 256; + + void SetUp(benchmark::State&) override + { + m_previousDataAccessor = g_dataAccessor; + m_previousAssetLibrary = g_activeAssetLibrary; + + m_rootUuids.clear(); + m_rootUuids.reserve(ROOT_ASSET_COUNT); + m_leafUuids.clear(); + m_leafUuids.reserve(ROOT_ASSET_COUNT * REFS_PER_ROOT); + + eastl::vector uuids; + eastl::vector> flatbuffers; + eastl::vector> hardReferences; + uuids.reserve(ROOT_ASSET_COUNT * (1 + REFS_PER_ROOT)); + flatbuffers.reserve(uuids.capacity()); + hardReferences.reserve(uuids.capacity()); + + m_rootTotalBytes.clear(); + m_rootTotalBytes.reserve(ROOT_ASSET_COUNT); + + for (std::uint32_t i = 0; i < ROOT_ASSET_COUNT; ++i) + { + BenchmarkAsset root; + root.SetName(root.GetHeader().uuid.ToString().c_str()); + root.GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB}); + + std::size_t totalBytes = 0; + for (std::uint32_t j = 0; j < REFS_PER_ROOT; ++j) + { + BenchmarkAssetReferred leaf; + leaf.SetName(leaf.GetHeader().uuid.ToString().c_str()); + + root.GetAsset().refs.emplace_back(leaf.GetHeader().uuid); + + const eastl::vector leafFlatbuffer = leaf.Pack(); + totalBytes += leafFlatbuffer.size(); + m_leafUuids.push_back(leaf.GetHeader().uuid); + uuids.push_back(leaf.GetHeader().uuid); + hardReferences.push_back(leaf.GetHardReferences(leafFlatbuffer)); + flatbuffers.push_back(leafFlatbuffer); + } + + const eastl::vector rootFlatbuffer = root.Pack(); + totalBytes += rootFlatbuffer.size(); + m_rootTotalBytes.emplace(root.GetHeader().uuid, totalBytes); + m_rootUuids.push_back(root.GetHeader().uuid); + uuids.push_back(root.GetHeader().uuid); + hardReferences.push_back(root.GetHardReferences(rootFlatbuffer)); + flatbuffers.push_back(rootFlatbuffer); + } + +#if defined BIGFOOT_TOOLMODE + m_bigFile.emplace(File {BIGFILE_ENGINEBENCHMARKS_LOCATION}); + m_bigFile->Clear(); + m_bigFile->SaveAssetFlatbuffers(uuids, flatbuffers, hardReferences); + + SetDataAccessor(&*m_bigFile); +#elif defined BIGFOOT_GAME + std::size_t totalFlatbufferBytes = 0; + std::size_t totalHardReferencesBytes = 0; + for (std::size_t i = 0; i < uuids.size(); ++i) + { + totalFlatbufferBytes += flatbuffers[i].size(); + totalHardReferencesBytes += hardReferences[i].size() * UUID::UUID_BYTE_SIZE; + } + + File {SASQUATCH_ENGINEBENCHMARKS_LOCATION}.Delete(); + { + SasquatchWriter writer {File {SASQUATCH_ENGINEBENCHMARKS_LOCATION}, + uuids.size(), + totalFlatbufferBytes, + totalHardReferencesBytes}; + for (std::size_t i = 0; i < uuids.size(); ++i) + { + writer.WriteAsset(uuids[i], flatbuffers[i], hardReferences[i]); + } + } + + m_sasquatch.emplace(File {SASQUATCH_ENGINEBENCHMARKS_LOCATION}); + SetDataAccessor(&*m_sasquatch); +#endif + } + + void TearDown(benchmark::State&) override + { +#if defined BIGFOOT_TOOLMODE + m_bigFile.reset(); +#elif defined BIGFOOT_GAME + m_sasquatch.reset(); +#endif + SetDataAccessor(m_previousDataAccessor); + SetActiveAssetLibrary(m_previousAssetLibrary); + } + + /* + * Registers a fresh, empty AssetLibrary as the active one, so that every root and leaf asset appears unloaded + * + * \param p_library The library to register + * \param p_leafContainer The container to register leaves into + * \param p_rootContainer The container to register roots into + */ + static void ResetActiveLibrary(AssetLibrary& p_library, + AssetContainer& p_leafContainer, + AssetContainer& p_rootContainer) + { + std::ignore = p_library.Insert(&p_leafContainer); + std::ignore = p_library.Insert(&p_rootContainer); + SetActiveAssetLibrary(&p_library); + } + + [[nodiscard]] + eastl::vector RandomRootBatch(const std::size_t p_batchSize) const + { + eastl::vector batch; + batch.reserve(p_batchSize); + for (std::size_t i = 0; i < p_batchSize; ++i) + { + batch.push_back(m_rootUuids[Random::get(0, m_rootUuids.size() - 1)]); + } + + return batch; + } + + /* + * Total flatbuffer bytes (root + its leaves) that loading p_roots will actually read, deduplicated the same way + * SerializationManager::LoadAssets deduplicates its own transitive discovery + * + * \param p_roots The root assets that would be loaded + * \return The total flatbuffer bytes across the unique roots + */ + [[nodiscard]] + std::int64_t TotalBytes(const eastl::vector& p_roots) const + { + ankerl::unordered_dense::set unique; + unique.reserve(p_roots.size()); + for (const UUID& uuid: p_roots) + { + unique.insert(uuid); + } + + std::int64_t bytes = 0; + for (const UUID& uuid: unique) + { + bytes += static_cast(m_rootTotalBytes.at(uuid)); + } + + return bytes; + } + + DataAccessor* m_previousDataAccessor = nullptr; + AssetLibrary* m_previousAssetLibrary = nullptr; + + eastl::vector m_rootUuids; + eastl::vector m_leafUuids; + ankerl::unordered_dense::map m_rootTotalBytes; + +#if defined BIGFOOT_TOOLMODE + eastl::optional m_bigFile; +#elif defined BIGFOOT_GAME + eastl::optional m_sasquatch; +#endif +}; + +/****************************************************************************************/ + +BENCHMARK_DEFINE_F(SerializationManagerFixture, LoadAsset)(benchmark::State& state) +{ + std::int64_t bytesProcessed = 0; + + for (auto _: state) + { + state.PauseTiming(); + AssetLibrary library; + // leafContainer must outlive rootContainer: the loaded root's HardReferences release into leafContainer's + // handles on destruction, so declaring it second would tear it down first and use-after-free those handles. + AssetContainer leafContainer; + AssetContainer rootContainer; + ResetActiveLibrary(library, leafContainer, rootContainer); + + const UUID uuid = m_rootUuids[Random::get(0, m_rootUuids.size() - 1)]; + const std::size_t uuidBytes = m_rootTotalBytes.at(uuid); + state.ResumeTiming(); + + SerializationManager::LoadAsset(uuid); + bytesProcessed += static_cast(uuidBytes); + } + + state.SetItemsProcessed(state.iterations() * (1 + REFS_PER_ROOT)); + state.SetBytesProcessed(bytesProcessed); +} + +BENCHMARK_REGISTER_F(SerializationManagerFixture, LoadAsset)->Name("SerializationManager/LoadAsset")->UseRealTime(); + +/****************************************************************************************/ + +BENCHMARK_DEFINE_F(SerializationManagerFixture, LoadAssets)(benchmark::State& state) +{ + const auto batchSize = static_cast(state.range(0)); + std::int64_t bytesProcessed = 0; + + for (auto _: state) + { + state.PauseTiming(); + AssetLibrary library; + // leafContainer must outlive rootContainer, see the same comment in the LoadAsset benchmark above. + AssetContainer leafContainer; + AssetContainer rootContainer; + ResetActiveLibrary(library, leafContainer, rootContainer); + + const eastl::vector batch = RandomRootBatch(batchSize); + const std::int64_t batchBytes = TotalBytes(batch); + state.ResumeTiming(); + + SerializationManager::LoadAssets(batch); + bytesProcessed += batchBytes; + } + + state.SetItemsProcessed(state.iterations() * batchSize * (1 + REFS_PER_ROOT)); + state.SetBytesProcessed(bytesProcessed); +} + +BENCHMARK_REGISTER_F(SerializationManagerFixture, LoadAssets) + ->Name("SerializationManager/LoadAssets") + ->Arg(1) + ->Arg(8) + ->Arg(64) + ->Arg(512) + ->UseRealTime(); + +/****************************************************************************************/ + +#if defined BIGFOOT_TOOLMODE +BENCHMARK_DEFINE_F(SerializationManagerFixture, SaveAsset)(benchmark::State& state) +{ + AssetLibrary library; + AssetContainer container; + std::ignore = library.Insert(&container); + SetActiveAssetLibrary(&library); + + std::int64_t bytesProcessed = 0; + + for (auto _: state) + { + state.PauseTiming(); + auto* asset = static_cast(container.Insert()->GetAsset()); + asset->SetName(asset->GetHeader().uuid.ToString().c_str()); + asset->GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB}); + for (std::uint32_t j = 0; j < REFS_PER_ROOT; ++j) + { + asset->GetAsset().refs.emplace_back(m_leafUuids[Random::get(0, m_leafUuids.size() - 1)]); + } + const UUID uuid = asset->GetHeader().uuid; + const std::size_t assetBytes = asset->Pack().size(); + state.ResumeTiming(); + + benchmark::DoNotOptimize(SerializationManager::SaveAsset(uuid)); + bytesProcessed += static_cast(assetBytes); + } + + state.SetItemsProcessed(state.iterations()); + state.SetBytesProcessed(bytesProcessed); +} + +BENCHMARK_REGISTER_F(SerializationManagerFixture, SaveAsset)->Name("SerializationManager/SaveAsset")->UseRealTime(); + +/****************************************************************************************/ + +BENCHMARK_DEFINE_F(SerializationManagerFixture, SaveAssets)(benchmark::State& state) +{ + const auto batchSize = static_cast(state.range(0)); + + AssetLibrary library; + AssetContainer container; + std::ignore = library.Insert(&container); + SetActiveAssetLibrary(&library); + + std::int64_t bytesProcessed = 0; + + for (auto _: state) + { + state.PauseTiming(); + eastl::vector uuids; + uuids.reserve(batchSize); + std::size_t batchBytes = 0; + for (std::size_t i = 0; i < batchSize; ++i) + { + auto* asset = static_cast(container.Insert()->GetAsset()); + asset->SetName(asset->GetHeader().uuid.ToString().c_str()); + asset->GetAsset().payload.assign(ASSET_PAYLOAD_SIZE, std::uint8_t {0xAB}); + asset->GetAsset().refs.emplace_back(m_leafUuids[Random::get(0, m_leafUuids.size() - 1)]); + + batchBytes += asset->Pack().size(); + uuids.push_back(asset->GetHeader().uuid); + } + state.ResumeTiming(); + + benchmark::DoNotOptimize(SerializationManager::SaveAssets(uuids)); + bytesProcessed += static_cast(batchBytes); + } + + state.SetItemsProcessed(state.iterations() * batchSize); + state.SetBytesProcessed(bytesProcessed); +} + +BENCHMARK_REGISTER_F(SerializationManagerFixture, SaveAssets) + ->Name("SerializationManager/SaveAssets") + ->Arg(1) + ->Arg(8) + ->Arg(64) + ->Arg(512) + ->UseRealTime(); +#endif +} // namespace Bigfoot