diff --git a/Bigfoot/Benchmarks/CMakeLists.txt b/Bigfoot/Benchmarks/CMakeLists.txt new file mode 100644 index 0000000..992910a --- /dev/null +++ b/Bigfoot/Benchmarks/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Utils) \ No newline at end of file diff --git a/Bigfoot/Benchmarks/Utils/CMakeLists.txt b/Bigfoot/Benchmarks/Utils/CMakeLists.txt new file mode 100644 index 0000000..53a5d66 --- /dev/null +++ b/Bigfoot/Benchmarks/Utils/CMakeLists.txt @@ -0,0 +1,5 @@ +get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME) +project(${PackageName}Benchmarks) + +bigfoot_create_package_benchmarks( + "") \ No newline at end of file diff --git a/Bigfoot/Benchmarks/Utils/Container/SlotMap.cpp b/Bigfoot/Benchmarks/Utils/Container/SlotMap.cpp new file mode 100644 index 0000000..6fcfb7f --- /dev/null +++ b/Bigfoot/Benchmarks/Utils/Container/SlotMap.cpp @@ -0,0 +1,250 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace Bigfoot +{ +struct MyComplexStruct +{ + std::uint32_t m_actualValueForBench = 0; // field summed by the Iterate benchmark + + float m_position[3] = {0.0f, 0.0f, 0.0f}; + float m_rotation[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + float m_scale[3] = {1.0f, 1.0f, 1.0f}; + + std::uint64_t m_entityId = 0; + std::uint32_t m_parentSlot = 0; + std::uint32_t m_flags = 0; + + MyComplexStruct() = default; + + explicit MyComplexStruct(const std::uint32_t p_value): + m_actualValueForBench(p_value), + m_entityId(p_value) + { + } +}; + +static_assert(sizeof(MyComplexStruct) == 64, "Payload should be exactly one cache line."); +static_assert(std::is_trivially_copyable_v, + "Container moves should be a plain memcpy, not a heap-touching move."); + +class SlotMapAdaptor +{ + public: + using Key = SlotMap::SlotKey; + + Key Add(MyComplexStruct&& p_value) + { + return m_container.Insert(std::move(p_value)); + } + + const MyComplexStruct* Find(const Key p_key) const + { + return m_container.Get(p_key); + } + + void Remove(const Key p_key) + { + m_container.Remove(p_key); + } + + void Clear() + { + m_container.Reset(); + } + + template + void ForEachValue(FUNC&& p_func) const + { + for (const MyComplexStruct& value: m_container) + { + p_func(value); + } + } + + private: + SlotMap m_container; +}; + +template +class HashMapHarnessAdaptor +{ + public: + using Key = typename MAP::key_type; + + Key Add(MyComplexStruct&& p_value) + { + const Key key = m_nextKey++; + m_container.emplace(key, std::move(p_value)); + return key; + } + + const MyComplexStruct* Find(const Key p_key) const + { + const auto it = m_container.find(p_key); + return it != m_container.end() ? &it->second : nullptr; + } + + void Remove(const Key p_key) + { + m_container.erase(p_key); + } + + void Clear() + { + m_container.clear(); + m_nextKey = 0; + } + + template + void ForEachValue(FUNC&& p_func) const + { + for (const auto& keyValue: m_container) + { + p_func(keyValue.second); + } + } + + private: + MAP m_container; + Key m_nextKey = 0; +}; + +template +void Insert(benchmark::State& state) +{ + const auto count = static_cast(state.range(0)); + + for (auto _: state) + { + ADAPTOR adaptor; + for (std::uint32_t i = 0; i < count; ++i) + { + benchmark::DoNotOptimize(adaptor.Add(MyComplexStruct {i})); + } + benchmark::DoNotOptimize(adaptor); + benchmark::ClobberMemory(); + } + + state.SetItemsProcessed(state.iterations() * count); +} + +template +void Remove(benchmark::State& state) +{ + const auto count = static_cast(state.range(0)); + + ADAPTOR adaptor; + std::vector keys; + keys.reserve(count); + std::mt19937 rng(0x5EEDU); + + for (auto _: state) + { + state.PauseTiming(); + adaptor.Clear(); + keys.clear(); + for (std::uint32_t i = 0; i < count; ++i) + { + keys.push_back(adaptor.Add(MyComplexStruct {i})); + } + std::shuffle(keys.begin(), keys.end(), rng); + state.ResumeTiming(); + + for (const typename ADAPTOR::Key key: keys) + { + adaptor.Remove(key); + } + benchmark::DoNotOptimize(adaptor); + benchmark::ClobberMemory(); + } + + state.SetItemsProcessed(state.iterations() * count); +} + +template +void Get(benchmark::State& state) +{ + const auto count = static_cast(state.range(0)); + + ADAPTOR adaptor; + std::vector keys; + keys.reserve(count); + for (std::uint32_t i = 0; i < count; ++i) + { + keys.push_back(adaptor.Add(MyComplexStruct {i})); + } + std::mt19937 rng(0x5EEDU); + std::shuffle(keys.begin(), keys.end(), rng); + + for (auto _: state) + { + std::uint64_t sum = 0; + for (const typename ADAPTOR::Key key: keys) + { + if (const MyComplexStruct* const value = adaptor.Find(key)) + { + sum += value->m_actualValueForBench; + } + } + benchmark::DoNotOptimize(sum); + } + + state.SetItemsProcessed(state.iterations() * count); +} + +template +void Iterate(benchmark::State& state) +{ + const auto count = static_cast(state.range(0)); + + ADAPTOR adaptor; + for (std::uint32_t i = 0; i < count; ++i) + { + adaptor.Add(MyComplexStruct {i}); + } + + for (auto _: state) + { + std::uint64_t sum = 0; + adaptor.ForEachValue( + [&sum](const MyComplexStruct& value) + { + sum += value.m_actualValueForBench; + }); + benchmark::DoNotOptimize(sum); + } + + state.SetItemsProcessed(state.iterations() * count); +} + +// Register all four benchmarks for one container adapter under a readable name. +#define BIGFOOT_REGISTER_BENCHMARKS(ADAPTOR, NAME) \ + BENCHMARK_TEMPLATE(Insert, ADAPTOR)->Name(NAME "/Insert")->Range(8, 8 << 16); \ + BENCHMARK_TEMPLATE(Remove, ADAPTOR)->Name(NAME "/Remove")->Range(8, 8 << 16); \ + BENCHMARK_TEMPLATE(Get, ADAPTOR)->Name(NAME "/Get")->Range(8, 8 << 16); \ + BENCHMARK_TEMPLATE(Iterate, ADAPTOR)->Name(NAME "/Iterate")->Range(8, 8 << 16) + +using UnorderedDenseMapHarness = HashMapHarnessAdaptor>; +using UnorderedDenseSegementedMapHarness = + HashMapHarnessAdaptor>; +using UnorderedMapHarness = HashMapHarnessAdaptor>; +using MapHarness = HashMapHarnessAdaptor>; + +BIGFOOT_REGISTER_BENCHMARKS(SlotMapAdaptor, "Bigfoot::SlotMap"); +BIGFOOT_REGISTER_BENCHMARKS(UnorderedDenseMapHarness, "ankerl::unordered_dense::map"); +BIGFOOT_REGISTER_BENCHMARKS(UnorderedDenseSegementedMapHarness, "ankerl::unordered_dense::segmented_map"); +BIGFOOT_REGISTER_BENCHMARKS(UnorderedMapHarness, "std::unordered_map"); +BIGFOOT_REGISTER_BENCHMARKS(MapHarness, "std::map"); + +#undef BIGFOOT_REGISTER_BENCHMARKS +} // namespace Bigfoot diff --git a/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp b/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp index ef7a6c9..f4f39a1 100644 --- a/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp +++ b/Bigfoot/Sources/Utils/Include/Utils/Containers/SlotMap.hpp @@ -19,7 +19,7 @@ namespace Bigfoot template && std::is_integral_v, bool> = true> + std::enable_if_t && std::is_unsigned_v, bool> = true> class SlotMap { public: @@ -43,42 +43,42 @@ class SlotMap static constexpr IndexType MAX_INDEX = std::numeric_limits::max(); - SlotKey(const VersionType p_version, const IndexType p_index): + constexpr SlotKey(const VersionType p_version, const IndexType p_index): m_key((static_cast(p_version) << INDEX_BIT_COUNT) | (static_cast(p_index) & INDEX_MASK)) { } - SlotKey(): + constexpr SlotKey(): SlotKey(INVALID_VERSION, 0) { } - SlotKey(const SlotKey& p_slotKey) = default; - SlotKey(SlotKey&& p_slotKey) = default; + constexpr SlotKey(const SlotKey& p_slotKey) = default; + constexpr SlotKey(SlotKey&& p_slotKey) = default; - ~SlotKey() = default; + constexpr ~SlotKey() = default; - bool Valid() const + constexpr bool Valid() const { return Version() != INVALID_VERSION; } - VersionType Version() const + constexpr VersionType Version() const { return static_cast(m_key >> INDEX_BIT_COUNT); } - IndexType Index() const + constexpr IndexType Index() const { return static_cast(m_key & INDEX_MASK); } - SlotKey& operator=(const SlotKey& p_slotKey) = default; - SlotKey& operator=(SlotKey&& p_slotKey) = default; + constexpr SlotKey& operator=(const SlotKey& p_slotKey) = default; + constexpr SlotKey& operator=(SlotKey&& p_slotKey) = default; [[nodiscard]] - bool operator==(const SlotKey& p_other) const = default; + constexpr bool operator==(const SlotKey& p_other) const = default; private: std::uint64_t m_key; diff --git a/Bigfoot/Tests/CMakeLists.txt b/Bigfoot/Tests/CMakeLists.txt index bdd68d7..8eca193 100644 --- a/Bigfoot/Tests/CMakeLists.txt +++ b/Bigfoot/Tests/CMakeLists.txt @@ -1,3 +1,3 @@ -add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/System) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Utils) +add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/System) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Engine) \ No newline at end of file diff --git a/Bigfoot/Tests/Utils/Containers/SlotMap.cpp b/Bigfoot/Tests/Utils/Containers/SlotMap.cpp index d28e755..80ce05e 100644 --- a/Bigfoot/Tests/Utils/Containers/SlotMap.cpp +++ b/Bigfoot/Tests/Utils/Containers/SlotMap.cpp @@ -10,208 +10,258 @@ namespace Bigfoot { -class SlotKeyFixture: public ::testing::Test +template +struct SlotMapConfig { - protected: + using Version = VERSION_TYPE; + using Index = INDEX_TYPE; +}; + +using SlotMapConfigs = ::testing::Types, + SlotMapConfig, + SlotMapConfig, + SlotMapConfig, + SlotMapConfig, + SlotMapConfig, + SlotMapConfig, + SlotMapConfig, + SlotMapConfig>; + +struct SlotMapConfigNames +{ + template + static std::string GetName(int) + { + return "VERSION" + std::to_string(sizeof(typename T::Version) * 8) + "_INDEX" + + std::to_string(sizeof(typename T::Index) * 8); + } }; /****************************************************************************************/ -TEST_F(SlotKeyFixture, DefaultSlotKeyIsInvalid) +template +class SlotKeyFixture: public ::testing::Test { - constexpr SlotMap::SlotKey::IndexType index = 0; - constexpr SlotMap::SlotKey::VersionType version = 0; + protected: + using SlotMapVersion = typename CONFIG::Version; + using SlotMapIndex = typename CONFIG::Index; + using SlotMapType = SlotMap; + using SlotKey = typename SlotMapType::SlotKey; +}; - SlotMap::SlotKey slotKey {}; +TYPED_TEST_SUITE(SlotKeyFixture, SlotMapConfigs, SlotMapConfigNames); + +/****************************************************************************************/ + +TYPED_TEST(SlotKeyFixture, DefaultSlotKeyIsInvalid) +{ + constexpr typename TestFixture::SlotMapIndex index = 0; + constexpr typename TestFixture::SlotMapVersion version = 0; + + constexpr typename TestFixture::SlotKey slotKey {}; EXPECT_FALSE(slotKey.Valid()); - EXPECT_EQ(slotKey.Version(), index); - EXPECT_EQ(slotKey.Index(), version); -} - -/****************************************************************************************/ - -TEST_F(SlotKeyFixture, Valid_ShouldReturnTrueIfTheSlotKeyIsValid) -{ - constexpr SlotMap::SlotKey::IndexType index = 0; - constexpr SlotMap::SlotKey::VersionType version = 1; - - SlotMap::SlotKey slotKey {version, index}; - EXPECT_TRUE(slotKey.Valid()); -} - -/****************************************************************************************/ - -TEST_F(SlotKeyFixture, Valid_ShouldReturnFalseIfTheSlotKeyIsValid) -{ - constexpr SlotMap::SlotKey::IndexType index = 0; - constexpr SlotMap::SlotKey::VersionType version = 0; - - SlotMap::SlotKey slotKey {version, index}; - EXPECT_FALSE(slotKey.Valid()); -} - -/****************************************************************************************/ - -TEST_F(SlotKeyFixture, Version_ShouldReturnTheVersion) -{ - constexpr SlotMap::SlotKey::IndexType index = 0; - constexpr SlotMap::SlotKey::VersionType version = 42; - - SlotMap::SlotKey slotKey {version, index}; EXPECT_EQ(slotKey.Version(), version); -} - -/****************************************************************************************/ - -TEST_F(SlotKeyFixture, Index_ShouldReturnTheIndex) -{ - constexpr SlotMap::SlotKey::IndexType index = 42; - constexpr SlotMap::SlotKey::VersionType version = 0; - - SlotMap::SlotKey slotKey {version, index}; EXPECT_EQ(slotKey.Index(), index); } /****************************************************************************************/ -class SlotMapFixture: public ::testing::Test +TYPED_TEST(SlotKeyFixture, Valid_ShouldReturnTrueIfTheSlotKeyIsValid) { - protected: - using SlotMapVersion = std::uint8_t; - using SlotMapIndex = std::uint32_t; + constexpr typename TestFixture::SlotMapIndex index = 0; + constexpr typename TestFixture::SlotMapVersion version = 1; - SlotMap m_slotMap; -}; - -/****************************************************************************************/ - -TEST_F(SlotMapFixture, Insert_ShouldReturnAValidSlotKey) -{ - const auto slotKey = m_slotMap.Insert(42); + constexpr typename TestFixture::SlotKey slotKey {version, index}; EXPECT_TRUE(slotKey.Valid()); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Insert_ShouldRecycleASlotKeyAfterARemove) +TYPED_TEST(SlotKeyFixture, Valid_ShouldReturnFalseIfTheSlotKeyIsValid) { - const auto slotKey = m_slotMap.Insert(42); - m_slotMap.Remove(slotKey); + constexpr typename TestFixture::SlotMapIndex index = 0; + constexpr typename TestFixture::SlotMapVersion version = 0; - const auto secondSlotKey = m_slotMap.Insert(69); + constexpr typename TestFixture::SlotKey slotKey {version, index}; + EXPECT_FALSE(slotKey.Valid()); +} + +/****************************************************************************************/ + +TYPED_TEST(SlotKeyFixture, Version_ShouldReturnTheVersion) +{ + constexpr typename TestFixture::SlotMapIndex index = 0; + constexpr typename TestFixture::SlotMapVersion version = 42; + + constexpr typename TestFixture::SlotKey slotKey {version, index}; + EXPECT_EQ(slotKey.Version(), version); +} + +/****************************************************************************************/ + +TYPED_TEST(SlotKeyFixture, Index_ShouldReturnTheIndex) +{ + constexpr typename TestFixture::SlotMapIndex index = 42; + constexpr typename TestFixture::SlotMapVersion version = 0; + + constexpr typename TestFixture::SlotKey slotKey {version, index}; + EXPECT_EQ(slotKey.Index(), index); +} + +/****************************************************************************************/ + +template +class SlotMapFixture: public ::testing::Test +{ + protected: + using SlotMapVersion = typename CONFIG::Version; + using SlotMapIndex = typename CONFIG::Index; + using SlotMapType = SlotMap; + using SlotKey = typename SlotMapType::SlotKey; + + SlotMapType m_slotMap; +}; + +TYPED_TEST_SUITE(SlotMapFixture, SlotMapConfigs, SlotMapConfigNames); + +/****************************************************************************************/ + +TYPED_TEST(SlotMapFixture, Insert_ShouldReturnAValidSlotKey) +{ + const auto slotKey = this->m_slotMap.Insert(42); + EXPECT_TRUE(slotKey.Valid()); +} + +/****************************************************************************************/ + +TYPED_TEST(SlotMapFixture, Insert_ShouldRecycleASlotKeyAfterARemove) +{ + const auto slotKey = this->m_slotMap.Insert(42); + this->m_slotMap.Remove(slotKey); + + const auto secondSlotKey = this->m_slotMap.Insert(69); EXPECT_NE(secondSlotKey.Version(), slotKey.Version()); EXPECT_EQ(secondSlotKey.Index(), slotKey.Index()); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Has_ShouldReturnTrueIfTheSlotMapHasTheKey) +TYPED_TEST(SlotMapFixture, Has_ShouldReturnTrueIfTheSlotMapHasTheKey) { - const auto slotKey = m_slotMap.Insert(42); - EXPECT_TRUE(m_slotMap.Has(slotKey)); + const auto slotKey = this->m_slotMap.Insert(42); + EXPECT_TRUE(this->m_slotMap.Has(slotKey)); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Has_ShouldReturnFalseIfTheSlotMapDoesNotHaveTheKey) +TYPED_TEST(SlotMapFixture, Has_ShouldReturnFalseIfTheSlotMapDoesNotHaveTheKey) { - const auto slotKey = m_slotMap.Insert(42); - m_slotMap.Remove(slotKey); - EXPECT_FALSE(m_slotMap.Has(slotKey)); - EXPECT_FALSE(m_slotMap.Has((SlotMap::SlotKey {1, 22}))); - EXPECT_FALSE(m_slotMap.Has((SlotMap::SlotKey {}))); + const auto slotKey = this->m_slotMap.Insert(42); + this->m_slotMap.Remove(slotKey); + EXPECT_FALSE(this->m_slotMap.Has(slotKey)); + EXPECT_FALSE(this->m_slotMap.Has(typename TestFixture::SlotKey {1, 22})); + EXPECT_FALSE(this->m_slotMap.Has(typename TestFixture::SlotKey {})); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Remove_ShouldRemoveTheSlotKey) +TYPED_TEST(SlotMapFixture, Remove_ShouldRemoveTheSlotKey) { - const auto slotKey = m_slotMap.Insert(42); - m_slotMap.Remove(slotKey); - EXPECT_FALSE(m_slotMap.Has(slotKey)); + const auto slotKey = this->m_slotMap.Insert(42); + this->m_slotMap.Remove(slotKey); + EXPECT_FALSE(this->m_slotMap.Has(slotKey)); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Remove_ShouldNotRecycleASlotWhenVersionWasExhausted) +TYPED_TEST(SlotMapFixture, Remove_ShouldNotRecycleASlotWhenVersionWasExhausted) { - auto key = m_slotMap.Insert(1); - - for (SlotMapVersion i = 1; i < std::numeric_limits::max(); ++i) + if constexpr (std::is_same_v) { - m_slotMap.Remove(key); - const auto newKey = m_slotMap.Insert(1); - EXPECT_EQ(newKey.Index(), key.Index()); - key = newKey; + GTEST_SKIP() << "Skipped for 32-bit version: exhausting all versions is too slow."; } + else + { + auto key = this->m_slotMap.Insert(1); - // Slot is at MAX_VERSION — one more remove should overflow and permanently deactivate it - EXPECT_EQ(key.Version(), std::numeric_limits::max()); - m_slotMap.Remove(key); + for (typename TestFixture::SlotMapVersion i = 1; + i < std::numeric_limits::max(); + ++i) + { + this->m_slotMap.Remove(key); + const auto newKey = this->m_slotMap.Insert(1); + EXPECT_EQ(newKey.Index(), key.Index()); + key = newKey; + } - EXPECT_FALSE(m_slotMap.Has(key)); + // Slot is at MAX_VERSION — one more remove should overflow and permanently deactivate it + EXPECT_EQ(key.Version(), std::numeric_limits::max()); + this->m_slotMap.Remove(key); - // Dead slot must not be recycled; a new insert must allocate a fresh slot index - const auto newKey = m_slotMap.Insert(2); - EXPECT_NE(newKey.Index(), key.Index()); + EXPECT_FALSE(this->m_slotMap.Has(key)); - // Ensure an invalid key does not return an exhausted slot - EXPECT_EQ(m_slotMap.Get(SlotMap::SlotKey {}), nullptr); + // Dead slot must not be recycled; a new insert must allocate a fresh slot index + const auto newKey = this->m_slotMap.Insert(2); + EXPECT_NE(newKey.Index(), key.Index()); + + // Ensure an invalid key does not return an exhausted slot + EXPECT_EQ(this->m_slotMap.Get(typename TestFixture::SlotKey {}), nullptr); + } } /****************************************************************************************/ -TEST_F(SlotMapFixture, Remove_ShouldNotDoAnythingInCaseOfDoubleRemove) +TYPED_TEST(SlotMapFixture, Remove_ShouldNotDoAnythingInCaseOfDoubleRemove) { - const auto slotKey1 = m_slotMap.Insert(42); - const auto slotKey2 = m_slotMap.Insert(69); + const auto slotKey1 = this->m_slotMap.Insert(42); + const auto slotKey2 = this->m_slotMap.Insert(69); - m_slotMap.Remove(slotKey1); - m_slotMap.Remove(slotKey1); + this->m_slotMap.Remove(slotKey1); + this->m_slotMap.Remove(slotKey1); - EXPECT_EQ(*m_slotMap.Get(slotKey2), 69); + EXPECT_EQ(*this->m_slotMap.Get(slotKey2), 69); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Remove_ShouldNotDoAnythingInCaseStaleKey) +TYPED_TEST(SlotMapFixture, Remove_ShouldNotDoAnythingInCaseStaleKey) { - const auto slotKey1 = m_slotMap.Insert(42); + const auto slotKey1 = this->m_slotMap.Insert(42); - m_slotMap.Remove(SlotMap::SlotKey {2, 0}); + this->m_slotMap.Remove(typename TestFixture::SlotKey {2, 0}); - EXPECT_EQ(*m_slotMap.Get(slotKey1), 42); + EXPECT_EQ(*this->m_slotMap.Get(slotKey1), 42); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Get_ShouldReturnNullptrForInvalidSlotKeys) +TYPED_TEST(SlotMapFixture, Get_ShouldReturnNullptrForInvalidSlotKeys) { - const auto slotKey = m_slotMap.Insert(42); - m_slotMap.Remove(slotKey); + const auto slotKey = this->m_slotMap.Insert(42); + this->m_slotMap.Remove(slotKey); const auto validate = [&](auto& p_slotMap) { EXPECT_EQ(p_slotMap.Get(slotKey), nullptr); - EXPECT_EQ(p_slotMap.Get(SlotMap::SlotKey {1, 3}), nullptr); - EXPECT_EQ(p_slotMap.Get(SlotMap::SlotKey {}), nullptr); + EXPECT_EQ(p_slotMap.Get(typename TestFixture::SlotKey {1, 3}), nullptr); + EXPECT_EQ(p_slotMap.Get(typename TestFixture::SlotKey {}), nullptr); }; - validate(m_slotMap); - const auto& constSlotMap = m_slotMap; + validate(this->m_slotMap); + const auto& constSlotMap = this->m_slotMap; validate(constSlotMap); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Get_ShouldReturnTheValueForValidSlotKeys) +TYPED_TEST(SlotMapFixture, Get_ShouldReturnTheValueForValidSlotKeys) { - const auto slotKey1 = m_slotMap.Insert(42); - const auto slotKey2 = m_slotMap.Insert(69); - const auto slotKey3 = m_slotMap.Insert(28); - const auto slotKey4 = m_slotMap.Insert(0); + const auto slotKey1 = this->m_slotMap.Insert(42); + const auto slotKey2 = this->m_slotMap.Insert(69); + const auto slotKey3 = this->m_slotMap.Insert(28); + const auto slotKey4 = this->m_slotMap.Insert(0); - m_slotMap.Remove(slotKey2); + this->m_slotMap.Remove(slotKey2); const auto validate = [&](auto& p_slotMap) { @@ -220,150 +270,150 @@ TEST_F(SlotMapFixture, Get_ShouldReturnTheValueForValidSlotKeys) EXPECT_EQ(*p_slotMap.Get(slotKey4), 0); }; - validate(m_slotMap); - const auto& constSlotMap = m_slotMap; + validate(this->m_slotMap); + const auto& constSlotMap = this->m_slotMap; validate(constSlotMap); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Reset_ResetsTheSlotMapAndMakesCollisionWithOldSlotKeys) +TYPED_TEST(SlotMapFixture, Reset_ResetsTheSlotMapAndMakesCollisionWithOldSlotKeys) { - const auto slotKey1 = m_slotMap.Insert(42); - std::ignore = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + const auto slotKey1 = this->m_slotMap.Insert(42); + std::ignore = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - m_slotMap.Reset(); + this->m_slotMap.Reset(); - const auto slotKey5 = m_slotMap.Insert(128); + const auto slotKey5 = this->m_slotMap.Insert(128); EXPECT_EQ(slotKey1, slotKey5); - EXPECT_EQ(*m_slotMap.Get(slotKey5), 128); - EXPECT_EQ(*m_slotMap.Get(slotKey1), 128); + EXPECT_EQ(*this->m_slotMap.Get(slotKey5), 128); + EXPECT_EQ(*this->m_slotMap.Get(slotKey1), 128); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Clear_ResetsTheSlotMapAndButGuaranteesNotCollisionWithOldSlotKeys) +TYPED_TEST(SlotMapFixture, Clear_ResetsTheSlotMapAndButGuaranteesNotCollisionWithOldSlotKeys) { - const auto slotKey1 = m_slotMap.Insert(42); - std::ignore = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + const auto slotKey1 = this->m_slotMap.Insert(42); + std::ignore = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - m_slotMap.Clear(); + this->m_slotMap.Clear(); - const auto slotKey5 = m_slotMap.Insert(128); + const auto slotKey5 = this->m_slotMap.Insert(128); EXPECT_NE(slotKey1, slotKey5); - EXPECT_EQ(*m_slotMap.Get(slotKey5), 128); - EXPECT_EQ(m_slotMap.Get(slotKey1), nullptr); + EXPECT_EQ(*this->m_slotMap.Get(slotKey5), 128); + EXPECT_EQ(this->m_slotMap.Get(slotKey1), nullptr); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Size_ReturnTheSizeOfTheSlotMap) +TYPED_TEST(SlotMapFixture, Size_ReturnTheSizeOfTheSlotMap) { - EXPECT_EQ(m_slotMap.Size(), 0); + EXPECT_EQ(this->m_slotMap.Size(), 0); - std::ignore = m_slotMap.Insert(42); - const auto slotKey2 = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + const auto slotKey2 = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - EXPECT_EQ(m_slotMap.Size(), 4); + EXPECT_EQ(this->m_slotMap.Size(), 4); - m_slotMap.Remove(slotKey2); + this->m_slotMap.Remove(slotKey2); - EXPECT_EQ(m_slotMap.Size(), 3); + EXPECT_EQ(this->m_slotMap.Size(), 3); - m_slotMap.Clear(); + this->m_slotMap.Clear(); - EXPECT_EQ(m_slotMap.Size(), 0); + EXPECT_EQ(this->m_slotMap.Size(), 0); - std::ignore = m_slotMap.Insert(42); - std::ignore = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + std::ignore = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - EXPECT_EQ(m_slotMap.Size(), 4); + EXPECT_EQ(this->m_slotMap.Size(), 4); - m_slotMap.Reset(); + this->m_slotMap.Reset(); - EXPECT_EQ(m_slotMap.Size(), 0); + EXPECT_EQ(this->m_slotMap.Size(), 0); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Empty_ShouldReturnTrueIfTheSlotMapIsEmpty) +TYPED_TEST(SlotMapFixture, Empty_ShouldReturnTrueIfTheSlotMapIsEmpty) { - EXPECT_TRUE(m_slotMap.Empty()); + EXPECT_TRUE(this->m_slotMap.Empty()); - std::ignore = m_slotMap.Insert(42); - std::ignore = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + std::ignore = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - m_slotMap.Clear(); + this->m_slotMap.Clear(); - EXPECT_TRUE(m_slotMap.Empty()); + EXPECT_TRUE(this->m_slotMap.Empty()); - std::ignore = m_slotMap.Insert(42); - std::ignore = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + std::ignore = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - m_slotMap.Reset(); + this->m_slotMap.Reset(); - EXPECT_TRUE(m_slotMap.Empty()); + EXPECT_TRUE(this->m_slotMap.Empty()); - const auto slotKey9 = m_slotMap.Insert(42); - m_slotMap.Remove(slotKey9); + const auto slotKey9 = this->m_slotMap.Insert(42); + this->m_slotMap.Remove(slotKey9); - EXPECT_TRUE(m_slotMap.Empty()); + EXPECT_TRUE(this->m_slotMap.Empty()); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Empty_ShouldReturnFalseIfTheSlotMapIsNotEmpty) +TYPED_TEST(SlotMapFixture, Empty_ShouldReturnFalseIfTheSlotMapIsNotEmpty) { - std::ignore = m_slotMap.Insert(42); - std::ignore = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + std::ignore = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - EXPECT_FALSE(m_slotMap.Empty()); + EXPECT_FALSE(this->m_slotMap.Empty()); - m_slotMap.Clear(); + this->m_slotMap.Clear(); - std::ignore = m_slotMap.Insert(42); - std::ignore = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + std::ignore = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - EXPECT_FALSE(m_slotMap.Empty()); + EXPECT_FALSE(this->m_slotMap.Empty()); - m_slotMap.Reset(); + this->m_slotMap.Reset(); - const auto slotKey9 = m_slotMap.Insert(42); - EXPECT_FALSE(m_slotMap.Empty()); - m_slotMap.Remove(slotKey9); + const auto slotKey9 = this->m_slotMap.Insert(42); + EXPECT_FALSE(this->m_slotMap.Empty()); + this->m_slotMap.Remove(slotKey9); } /****************************************************************************************/ -TEST_F(SlotMapFixture, Iterator) +TYPED_TEST(SlotMapFixture, Iterator) { - const auto slotKey1 = m_slotMap.Insert(42); - const auto slotKey2 = m_slotMap.Insert(69); - const auto slotKey3 = m_slotMap.Insert(28); - const auto slotKey4 = m_slotMap.Insert(0); + const auto slotKey1 = this->m_slotMap.Insert(42); + const auto slotKey2 = this->m_slotMap.Insert(69); + const auto slotKey3 = this->m_slotMap.Insert(28); + const auto slotKey4 = this->m_slotMap.Insert(0); - m_slotMap.Remove(slotKey2); + this->m_slotMap.Remove(slotKey2); eastl::vector values; - for (auto it = m_slotMap.begin(); it != m_slotMap.end(); ++it) + for (auto it = this->m_slotMap.begin(); it != this->m_slotMap.end(); ++it) { values.push_back(*it); } @@ -374,28 +424,28 @@ TEST_F(SlotMapFixture, Iterator) EXPECT_EQ(values[2], 28); // The non-const iterator is mutable. - for (std::uint32_t& value: m_slotMap) + for (std::uint32_t& value: this->m_slotMap) { value += 100; } - EXPECT_EQ(*m_slotMap.Get(slotKey1), 142); - EXPECT_EQ(*m_slotMap.Get(slotKey3), 128); - EXPECT_EQ(*m_slotMap.Get(slotKey4), 100); + EXPECT_EQ(*this->m_slotMap.Get(slotKey1), 142); + EXPECT_EQ(*this->m_slotMap.Get(slotKey3), 128); + EXPECT_EQ(*this->m_slotMap.Get(slotKey4), 100); } /****************************************************************************************/ -TEST_F(SlotMapFixture, ConstIterator) +TYPED_TEST(SlotMapFixture, ConstIterator) { - std::ignore = m_slotMap.Insert(42); - const auto slotKey2 = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + const auto slotKey2 = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - m_slotMap.Remove(slotKey2); + this->m_slotMap.Remove(slotKey2); - const auto& constSlotMap = m_slotMap; + const auto& constSlotMap = this->m_slotMap; eastl::vector values; for (auto it = constSlotMap.begin(); it != constSlotMap.end(); ++it) @@ -410,7 +460,7 @@ TEST_F(SlotMapFixture, ConstIterator) // cbegin/cend yield the same const traversal. eastl::vector cValues; - for (auto it = m_slotMap.cbegin(); it != m_slotMap.cend(); ++it) + for (auto it = this->m_slotMap.cbegin(); it != this->m_slotMap.cend(); ++it) { cValues.push_back(*it); } @@ -420,17 +470,17 @@ TEST_F(SlotMapFixture, ConstIterator) /****************************************************************************************/ -TEST_F(SlotMapFixture, ReverseIterator) +TYPED_TEST(SlotMapFixture, ReverseIterator) { - const auto slotKey1 = m_slotMap.Insert(42); - const auto slotKey2 = m_slotMap.Insert(69); - const auto slotKey3 = m_slotMap.Insert(28); - const auto slotKey4 = m_slotMap.Insert(0); + const auto slotKey1 = this->m_slotMap.Insert(42); + const auto slotKey2 = this->m_slotMap.Insert(69); + const auto slotKey3 = this->m_slotMap.Insert(28); + const auto slotKey4 = this->m_slotMap.Insert(0); - m_slotMap.Remove(slotKey2); + this->m_slotMap.Remove(slotKey2); eastl::vector values; - for (auto it = m_slotMap.rbegin(); it != m_slotMap.rend(); ++it) + for (auto it = this->m_slotMap.rbegin(); it != this->m_slotMap.rend(); ++it) { values.push_back(*it); } @@ -441,28 +491,28 @@ TEST_F(SlotMapFixture, ReverseIterator) EXPECT_EQ(values[2], 42); // The non-const reverse iterator is mutable. - for (auto it = m_slotMap.rbegin(); it != m_slotMap.rend(); ++it) + for (auto it = this->m_slotMap.rbegin(); it != this->m_slotMap.rend(); ++it) { *it += 100; } - EXPECT_EQ(*m_slotMap.Get(slotKey1), 142); - EXPECT_EQ(*m_slotMap.Get(slotKey3), 128); - EXPECT_EQ(*m_slotMap.Get(slotKey4), 100); + EXPECT_EQ(*this->m_slotMap.Get(slotKey1), 142); + EXPECT_EQ(*this->m_slotMap.Get(slotKey3), 128); + EXPECT_EQ(*this->m_slotMap.Get(slotKey4), 100); } /****************************************************************************************/ -TEST_F(SlotMapFixture, ConstReverseIterator) +TYPED_TEST(SlotMapFixture, ConstReverseIterator) { - std::ignore = m_slotMap.Insert(42); - const auto slotKey2 = m_slotMap.Insert(69); - std::ignore = m_slotMap.Insert(28); - std::ignore = m_slotMap.Insert(0); + std::ignore = this->m_slotMap.Insert(42); + const auto slotKey2 = this->m_slotMap.Insert(69); + std::ignore = this->m_slotMap.Insert(28); + std::ignore = this->m_slotMap.Insert(0); - m_slotMap.Remove(slotKey2); + this->m_slotMap.Remove(slotKey2); - const auto& constSlotMap = m_slotMap; + const auto& constSlotMap = this->m_slotMap; eastl::vector values; for (auto it = constSlotMap.rbegin(); it != constSlotMap.rend(); ++it) @@ -477,7 +527,7 @@ TEST_F(SlotMapFixture, ConstReverseIterator) // crbegin/crend yield the same const reverse traversal. eastl::vector crValues; - for (auto it = m_slotMap.crbegin(); it != m_slotMap.crend(); ++it) + for (auto it = this->m_slotMap.crbegin(); it != this->m_slotMap.crend(); ++it) { crValues.push_back(*it); } diff --git a/CMake/Package.cmake b/CMake/Package.cmake index 4b91db9..dadf476 100644 --- a/CMake/Package.cmake +++ b/CMake/Package.cmake @@ -151,4 +151,103 @@ function(bigfoot_create_package_tests ParentFolder) add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}Fixture) set_target_properties(${PROJECT_NAME}Fixture PROPERTIES FOLDER UtilityTargets/Tests/Bigfoot/${ParentFolder}) +endfunction() + +function(bigfoot_create_package_benchmarks ParentFolder) + add_executable(${PROJECT_NAME}) + + target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20) + + target_link_libraries(${PROJECT_NAME} + PRIVATE + BigfootCompileAndLinkFlags + $ + benchmark::benchmark_main) + + bigfoot_compile_flatbuffers() + + file(GLOB_RECURSE _SOURCES + CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp + ) + + file(GLOB_RECURSE _HEADERS + CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.h + ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp + ) + + file(GLOB_RECURSE _OTHERS + CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp.in + ${CMAKE_CURRENT_SOURCE_DIR}/*.fbs + ${CMAKE_CURRENT_SOURCE_DIR}/*.sql + ) + + target_sources(${PROJECT_NAME} + PRIVATE + ${_SOURCES} + ${_OTHERS} + PUBLIC + FILE_SET HEADERS + BASE_DIRS + ${CMAKE_CURRENT_SOURCE_DIR}/Include + FILES + ${_HEADERS} + ) + + source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX Src/ FILES ${_SOURCES} ${_HEADERS} ${_OTHERS}) + + set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Benchmarks/Bigfoot/${ParentFolder}) + set_target_properties(${PROJECT_NAME} PROPERTIES VS_DEBUGGER_WORKING_DIRECTORY "$") + + ##################ASAN SETUP################### + + if(${ASAN}) + if(MSVC) + get_filename_component(MSVC_BIN_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY) + set(ASAN_DLL "${MSVC_BIN_DIR}/clang_rt.asan_dynamic-x86_64.dll") + + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp" + DEPENDS "${ASAN_DLL}" + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ASAN_DLL}" "$" + COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp" + COMMENT "Copying ASan DLL for ${PROJECT_NAME}" + ) + + add_custom_target(${PROJECT_NAME}Asan + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp" + ) + + add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}Asan) + set_target_properties(${PROJECT_NAME}Asan PROPERTIES FOLDER UtilityTargets/Benchmarks/Bigfoot/${ParentFolder}) + endif() + endif() + + ##################COPY FIXTURE FOLDER################### + if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Fixture) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Fixture) + endif() + + file(GLOB_RECURSE FIXTURE_FILES + CONFIGURE_DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/Fixture/* + ) + + add_custom_command( + OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-fixture.timestamp" + DEPENDS ${FIXTURE_FILES} + COMMAND ${CMAKE_COMMAND} -E remove_directory $/Fixture + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Fixture $/Fixture + COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-fixture.timestamp" + COMMENT "Copying Fixture folder for ${PROJECT_NAME}" + ) + + add_custom_target(${PROJECT_NAME}Fixture + DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-fixture.timestamp" + ) + + add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}Fixture) + set_target_properties(${PROJECT_NAME}Fixture PROPERTIES FOLDER UtilityTargets/Benchmarks/Bigfoot/${ParentFolder}) endfunction() \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e2c8892..34673e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,9 @@ add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bigfoot/Sources) if(${BUILD_TESTS}) add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bigfoot/Tests) endif() +if(${BUILD_BENCHMARKS}) + add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bigfoot/Benchmarks) +endif() add_custom_target(NatVis SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Vendor/NatVis/EASTL/EASTL.natvis