diff --git a/README.md b/README.md index c893410b5..12d7a26dd 100644 --- a/README.md +++ b/README.md @@ -189,19 +189,19 @@ Dell XPS 13 out of the mid 2014): | Benchmark | EntityX (compile-time) | EnTT | |-----------|-------------|-------------| -| Create 10M entities | 0.1289s | **0.0423s** | -| Destroy 10M entities | 0.0531s | **0.0221s** | -| Standard view, 10M entities, one component | 0.0107s | **7.8e-08s** | -| Standard view, 10M entities, two components | **0.0113s** | 0.0142s | -| Standard view, 10M entities, two components
Half of the entities have all the components | 0.0078s | **0.0072s** | -| Standard view, 10M entities, two components
One of the entities has all the components | 0.0071s | **5.5e-07s** | -| Persistent view, 10M entities, two components | 0.0113s | **1.1e-07s** | -| Standard view, 10M entities, five components | **0.0091s** | 0.0352s | -| Persistent view, 10M entities, five components | 0.0091s | **2.5e-07s** | -| Standard view, 10M entities, ten components | **0.0105s** | 0.0780s | -| Standard view, 10M entities, ten components
Half of the entities have all the components | **0.0090s** | 0.0407s | -| Standard view, 10M entities, ten components
One of the entities has all the components | 0.0070s | **1.3e-06s** | -| Persistent view, 10M entities, ten components | 0.0105s | **5.0e-07s** | +| Create 1M entities | 0.0167s | **0.0076s** | +| Destroy 1M entities | 0.0053s | **0.0031s** | +| Standard view, 1M entities, one component | 0.0012s | **1.9e-07s** | +| Standard view, 1M entities, two components | **0.0012s** | 0.0013s | +| Standard view, 1M entities, two components
Half of the entities have all the components | 0.0009s | **0.0007s** | +| Standard view, 1M entities, two components
One of the entities has all the components | 0.0008s | **1.3e-06s** | +| Persistent view, 1M entities, two components | 0.0012s | **2.8e-07s** | +| Standard view, 1M entities, five components | **0.0010s** | 0.0034s | +| Persistent view, 1M entities, five components | 0.0010s | **2.8e-07s** | +| Standard view, 1M entities, ten components | **0.0011s** | 0.0075s | +| Standard view, 1M entities, ten components
Half of the entities have all the components | **0.0010s** | 0.0041s | +| Standard view, 1M entities, ten components
One of the entities has all the components | 0.0008s | **1.7e-06s** | +| Persistent view, 1M entities, ten components | 0.0011s | **3.0e-07s** | | Sort 150k entities, one component
Arrays are in reverse order | - | **0.0040s** | | Sort 150k entities, enforce permutation
Arrays are in reverse order | - | **0.0006s** | diff --git a/src/entt/entity/entt_traits.hpp b/src/entt/entity/entt_traits.hpp index d07afa63d..241329fb2 100644 --- a/src/entt/entity/entt_traits.hpp +++ b/src/entt/entity/entt_traits.hpp @@ -58,11 +58,11 @@ struct entt_traits { using version_type = std::uint16_t; /*! @brief Mask to use to get the entity number out of an identifier. */ - static constexpr auto entity_mask = 0xFFFFFF; + static constexpr auto entity_mask = 0xFFFFF; /*! @brief Mask to use to get the version out of an identifier. */ - static constexpr auto version_mask = 0xFF; + static constexpr auto version_mask = 0xFFF; /*! @brief Extent of the entity number within an identifier. */ - static constexpr auto entity_shift = 24; + static constexpr auto entity_shift = 20; }; diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index 9191c2fb6..1af8a6cad 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -233,7 +233,7 @@ public: assert(has(entity)); const auto entt = entity & traits_type::entity_mask; // we must get rid of the in-use bit for it's not part of the position - return reverse[entt] & ~in_use; + return reverse[entt] & traits_type::entity_mask; } /** @@ -278,10 +278,9 @@ public: assert(has(entity)); const auto entt = entity & traits_type::entity_mask; const auto back = direct.back() & traits_type::entity_mask; - const auto pos = reverse[entt] & ~in_use; - // the order matters: if back and entt are the same (for the sparse set - // has size 1), switching the two lines below doesn't work as expected - reverse[back] = pos | in_use; + // we must get rid of the in-use bit for it's not part of the position + const auto pos = reverse[entt] & traits_type::entity_mask; + reverse[back] = reverse[entt]; reverse[entt] = pos; // swapping isn't required here, we are getting rid of the last element direct[pos] = direct.back(); diff --git a/test/benchmark/benchmark.cpp b/test/benchmark/benchmark.cpp index 0162ac24d..c075fa6ba 100644 --- a/test/benchmark/benchmark.cpp +++ b/test/benchmark/benchmark.cpp @@ -34,11 +34,11 @@ private: TEST(Benchmark, Construct) { entt::DefaultRegistry registry; - std::cout << "Constructing 10000000 entities" << std::endl; + std::cout << "Constructing 1000000 entities" << std::endl; Timer timer; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { registry.create(); } @@ -49,9 +49,9 @@ TEST(Benchmark, Destroy) { entt::DefaultRegistry registry; std::vector entities{}; - std::cout << "Destroying 10000000 entities" << std::endl; + std::cout << "Destroying 1000000 entities" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { entities.push_back(registry.create()); } @@ -88,12 +88,12 @@ TEST(Benchmark, IterateCreateDeleteSingleComponent) { timer.elapsed(); } -TEST(Benchmark, IterateSingleComponent10M) { +TEST(Benchmark, IterateSingleComponent1M) { entt::DefaultRegistry registry; - std::cout << "Iterating over 10000000 entities, one component" << std::endl; + std::cout << "Iterating over 1000000 entities, one component" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { registry.create(); } @@ -102,12 +102,12 @@ TEST(Benchmark, IterateSingleComponent10M) { timer.elapsed(); } -TEST(Benchmark, IterateTwoComponents10M) { +TEST(Benchmark, IterateTwoComponents1M) { entt::DefaultRegistry registry; - std::cout << "Iterating over 10000000 entities, two components" << std::endl; + std::cout << "Iterating over 1000000 entities, two components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { registry.create(); } @@ -116,12 +116,12 @@ TEST(Benchmark, IterateTwoComponents10M) { timer.elapsed(); } -TEST(Benchmark, IterateTwoComponents10MHalf) { +TEST(Benchmark, IterateTwoComponents1MHalf) { entt::DefaultRegistry registry; - std::cout << "Iterating over 10000000 entities, two components, half of the entities have all the components" << std::endl; + std::cout << "Iterating over 1000000 entities, two components, half of the entities have all the components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { auto entity = registry.create(); if(i % 2) { registry.assign(entity); } } @@ -131,12 +131,12 @@ TEST(Benchmark, IterateTwoComponents10MHalf) { timer.elapsed(); } -TEST(Benchmark, IterateTwoComponents10MOne) { +TEST(Benchmark, IterateTwoComponents1MOne) { entt::DefaultRegistry registry; - std::cout << "Iterating over 10000000 entities, two components, only one entity has all the components" << std::endl; + std::cout << "Iterating over 1000000 entities, two components, only one entity has all the components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { auto entity = registry.create(); if(i == 5000000L) { registry.assign(entity); } } @@ -146,13 +146,13 @@ TEST(Benchmark, IterateTwoComponents10MOne) { timer.elapsed(); } -TEST(Benchmark, IterateTwoComponentsPersistent10M) { +TEST(Benchmark, IterateTwoComponentsPersistent1M) { entt::DefaultRegistry registry; registry.prepare(); - std::cout << "Iterating over 10000000 entities, two components, persistent view" << std::endl; + std::cout << "Iterating over 1000000 entities, two components, persistent view" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { registry.create(); } @@ -161,44 +161,12 @@ TEST(Benchmark, IterateTwoComponentsPersistent10M) { timer.elapsed(); } -TEST(Benchmark, IterateTwoComponentsPersistent10MHalf) { - entt::DefaultRegistry registry; - registry.prepare(); - - std::cout << "Iterating over 10000000 entities, two components, persistent view, half of the entities have all the components" << std::endl; - - for(std::uint64_t i = 0; i < 10000000L; i++) { - auto entity = registry.create(); - if(i % 2) { registry.assign(entity); } - } - - Timer timer; - registry.persistent().each([](auto, auto &...) {}); - timer.elapsed(); -} - -TEST(Benchmark, IterateTwoComponentsPersistent10MOne) { - entt::DefaultRegistry registry; - registry.prepare(); - - std::cout << "Iterating over 10000000 entities, two components, persistent view, only one entity has all the components" << std::endl; - - for(std::uint64_t i = 0; i < 10000000L; i++) { - auto entity = registry.create(); - if(i == 5000000L) { registry.assign(entity); } - } - - Timer timer; - registry.persistent().each([](auto, auto &...) {}); - timer.elapsed(); -} - -TEST(Benchmark, IterateFiveComponents10M) { +TEST(Benchmark, IterateFiveComponents1M) { entt::DefaultRegistry registry; - std::cout << "Iterating over 10000000 entities, five components" << std::endl; + std::cout << "Iterating over 1000000 entities, five components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { registry.create, Comp<2>, Comp<3>>(); } @@ -207,57 +175,43 @@ TEST(Benchmark, IterateFiveComponents10M) { timer.elapsed(); } -TEST(Benchmark, IterateTenComponents10M) { +TEST(Benchmark, IterateFiveComponents1MHalf) { entt::DefaultRegistry registry; - std::cout << "Iterating over 10000000 entities, ten components" << std::endl; + std::cout << "Iterating over 1000000 entities, five components, half of the entities have all the components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { - registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); - } - - Timer timer; - registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); - timer.elapsed(); -} - -TEST(Benchmark, IterateTenComponents10MHalf) { - entt::DefaultRegistry registry; - - std::cout << "Iterating over 10000000 entities, ten components, half of the entities have all the components" << std::endl; - - for(std::uint64_t i = 0; i < 10000000L; i++) { - auto entity = registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); + for(std::uint64_t i = 0; i < 1000000L; i++) { + auto entity = registry.create, Comp<2>, Comp<3>>(); if(i % 2) { registry.assign(entity); } } Timer timer; - registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); + registry.view, Comp<2>, Comp<3>>().each([](auto, auto &...) {}); timer.elapsed(); } -TEST(Benchmark, IterateTenComponents10MOne) { +TEST(Benchmark, IterateFiveComponents1MOne) { entt::DefaultRegistry registry; - std::cout << "Iterating over 10000000 entities, ten components, only one entity has all the components" << std::endl; + std::cout << "Iterating over 1000000 entities, five components, only one entity has all the components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { - auto entity = registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); + for(std::uint64_t i = 0; i < 1000000L; i++) { + auto entity = registry.create, Comp<2>, Comp<3>>(); if(i == 5000000L) { registry.assign(entity); } } Timer timer; - registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); + registry.view, Comp<2>, Comp<3>>().each([](auto, auto &...) {}); timer.elapsed(); } -TEST(Benchmark, IterateFiveComponentsPersistent10M) { +TEST(Benchmark, IterateFiveComponentsPersistent1M) { entt::DefaultRegistry registry; registry.prepare, Comp<2>, Comp<3>>(); - std::cout << "Iterating over 10000000 entities, five components, persistent view" << std::endl; + std::cout << "Iterating over 1000000 entities, five components, persistent view" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { registry.create, Comp<2>, Comp<3>>(); } @@ -266,46 +220,58 @@ TEST(Benchmark, IterateFiveComponentsPersistent10M) { timer.elapsed(); } -TEST(Benchmark, IterateTenComponentsPersistent10M) { +TEST(Benchmark, IterateTenComponents1M) { entt::DefaultRegistry registry; - registry.prepare, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); - std::cout << "Iterating over 10000000 entities, ten components, persistent view" << std::endl; + std::cout << "Iterating over 1000000 entities, ten components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); } Timer timer; - registry.persistent, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); + registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); timer.elapsed(); } -TEST(Benchmark, IterateTenComponentsPersistent10MHalf) { +TEST(Benchmark, IterateTenComponents1MHalf) { entt::DefaultRegistry registry; - registry.prepare, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); - std::cout << "Iterating over 10000000 entities, ten components, persistent view, half of the entities have all the components" << std::endl; + std::cout << "Iterating over 1000000 entities, ten components, half of the entities have all the components" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { + for(std::uint64_t i = 0; i < 1000000L; i++) { auto entity = registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); if(i % 2) { registry.assign(entity); } } Timer timer; - registry.persistent, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); + registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); timer.elapsed(); } -TEST(Benchmark, IterateTenComponentsPersistent10MOne) { +TEST(Benchmark, IterateTenComponents1MOne) { + entt::DefaultRegistry registry; + + std::cout << "Iterating over 1000000 entities, ten components, only one entity has all the components" << std::endl; + + for(std::uint64_t i = 0; i < 1000000L; i++) { + auto entity = registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); + if(i == 5000000L) { registry.assign(entity); } + } + + Timer timer; + registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); + timer.elapsed(); +} + +TEST(Benchmark, IterateTenComponentsPersistent1M) { entt::DefaultRegistry registry; registry.prepare, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); - std::cout << "Iterating over 10000000 entities, ten components, persistent view, only one entity has all the components" << std::endl; + std::cout << "Iterating over 1000000 entities, ten components, persistent view" << std::endl; - for(std::uint64_t i = 0; i < 10000000L; i++) { - auto entity = registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); - if(i == 5000000L) { registry.assign(entity); } + for(std::uint64_t i = 0; i < 1000000L; i++) { + registry.create, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(); } Timer timer; diff --git a/test/entt/entity/registry.cpp b/test/entt/entity/registry.cpp index f4f9289ce..9eaf4a215 100644 --- a/test/entt/entity/registry.cpp +++ b/test/entt/entity/registry.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include TEST(DefaultRegistry, Functionalities) { @@ -149,6 +150,22 @@ TEST(DefaultRegistry, CreateDestroyCornerCase) { ASSERT_EQ(registry.current(e1), entt::DefaultRegistry::version_type{1}); } +TEST(DefaultRegistry, VersionOverflow) { + entt::DefaultRegistry registry; + + auto entity = registry.create(); + registry.destroy(entity); + + ASSERT_EQ(registry.version(entity), entt::DefaultRegistry::version_type{}); + + for(auto i = entt::entt_traits::version_mask; i; --i) { + ASSERT_NE(registry.current(entity), registry.version(entity)); + registry.destroy(registry.create()); + } + + ASSERT_EQ(registry.current(entity), registry.version(entity)); +} + TEST(DefaultRegistry, Each) { entt::DefaultRegistry registry; entt::DefaultRegistry::size_type tot;