From 08dc2fcf337835eaf91cc22eed8abff1504e96fa Mon Sep 17 00:00:00 2001 From: Michele Caini Date: Thu, 26 Apr 2018 09:21:04 +0200 Subject: [PATCH] performance improvement --- README.md | 19 ++- TODO | 3 +- src/entt/entity/actor.hpp | 6 +- src/entt/entity/registry.hpp | 21 +-- src/entt/entity/snapshot.hpp | 2 +- src/entt/entity/sparse_set.hpp | 2 +- src/entt/entity/utility.hpp | 28 +--- src/entt/entity/view.hpp | 104 +++++++++----- test/benchmark/benchmark.cpp | 246 ++++++++++++++++++++++++++------- 9 files changed, 303 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index a7e9dacdb..7869b008e 100644 --- a/README.md +++ b/README.md @@ -1068,7 +1068,7 @@ All of them have pros and cons to take in consideration. In particular: * Once prepared, creating and destroying them isn't expensive at all because they don't have any type of initialization. - * They are the best tool for iterating entities for mmultiple components and + * They are the best tool for iterating entities for multiple components when most entities have them all. Cons: @@ -1100,12 +1100,21 @@ All of them have pros and cons to take in consideration. In particular: To sum up and as a rule of thumb: * Use a raw view to iterate components only (no entities) for a given type. -* Use a standard view to iterate entities for a single component. -* Use a standard view to iterate entities for multiple components when a - significantly low number of entities have one of the components. +* Use a standard view to iterate entities and components for a single type. +* Use a standard view to iterate entities and components for multiple types when + the number of types is low. Standard views are really optimized and persistent + views won't add much in this case. +* Use a standard view to iterate entities and components for multiple types when + a significantly low number of entities have one of the components. * Use a standard view in all those cases where a persistent view would give a boost to performance but the iteration isn't performed frequently. -* Prepare and use a persistent view in all the other cases. +* Prepare and use a persistent view when you want to iterate only entities for + multiple components. +* Prepare and use a persistent view when you want to iterate entities for + multiple components and each component is assigned to a great number of + entities but the intersection between the sets of entities is small. +* Prepare and use a persistent view in all the cases where a standard view + wouldn't fit well otherwise. To easily iterate entities and components, all the views offer the common `begin` and `end` member functions that allow users to use a view in a typical diff --git a/TODO b/TODO index 737c1a3ce..39cda52e9 100644 --- a/TODO +++ b/TODO @@ -5,7 +5,8 @@ * define basic reactive systems (track entities to which component is attached, track entities from which component is removed, and so on) * define systems as composable mixins (initializazion, reactive, update, whatever) with flexible auto-detected arguments (registry, views, etc) * does it worth it to add an optional functor to the member functions of snapshot so as to filter out instances and entities? +* allow for custom sort functions on registry/view sort (unlikely std::sort is the best function ever in game programming) * ease the assignment of tags as string (use a template class with a non-type template parameter behind the scene) -* dictionary based dependency class (templates copied over) + prefabs (shared state/copy-on-write) +* prototype entities, a really interesting feature (see #56) * "singleton mode" for tags (see #66) * AOB diff --git a/src/entt/entity/actor.hpp b/src/entt/entity/actor.hpp index d57ba92f1..492064ea1 100644 --- a/src/entt/entity/actor.hpp +++ b/src/entt/entity/actor.hpp @@ -143,7 +143,7 @@ struct Actor { * @return A reference to the instance of the tag owned by the actor. */ template - Tag & get(tag_t) ENTT_NOEXCEPT { + inline Tag & get(tag_t) ENTT_NOEXCEPT { return const_cast(const_cast(this)->get(tag_t{})); } @@ -163,7 +163,7 @@ struct Actor { * @return A reference to the instance of the component owned by the actor. */ template - Component & get() ENTT_NOEXCEPT { + inline Component & get() ENTT_NOEXCEPT { return const_cast(const_cast(this)->get()); } @@ -179,7 +179,7 @@ struct Actor { * @brief Returns a reference to the underlying registry. * @return A reference to the underlying registry. */ - registry_type & registry() ENTT_NOEXCEPT { + inline registry_type & registry() ENTT_NOEXCEPT { return const_cast(const_cast(this)->registry()); } diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp index e562a7c01..ff23fc0bc 100644 --- a/src/entt/entity/registry.hpp +++ b/src/entt/entity/registry.hpp @@ -44,14 +44,15 @@ class Registry { template static void creating(Registry ®istry, Entity entity) { - const auto ttype = handler_family::type(); - return registry.has(entity) ? registry.handlers[ttype]->construct(entity) : void(); + if(registry.has(entity)) { + registry.handlers[handler_family::type()]->construct(entity); + } } template static void destroying(Registry ®istry, Entity entity) { - auto &handler = registry.handlers[handler_family::type()]; - return handler->has(entity) ? handler->destroy(entity) : void(); + auto &handler = *registry.handlers[handler_family::type()]; + return handler.has(entity) ? handler.destroy(entity) : void(); } struct Attachee { @@ -85,7 +86,7 @@ class Registry { } template - SparseSet & pool() ENTT_NOEXCEPT { + inline SparseSet & pool() ENTT_NOEXCEPT { return const_cast &>(const_cast(this)->pool()); } @@ -153,7 +154,7 @@ public: * @return Runtime numeric identifier of the given type of tag. */ template - tag_type type(tag_t) const ENTT_NOEXCEPT { + static tag_type type(tag_t) ENTT_NOEXCEPT { return tag_family::type(); } @@ -170,7 +171,7 @@ public: * @return Runtime numeric identifier of the given type of component. */ template - component_type type() const ENTT_NOEXCEPT { + static component_type type() ENTT_NOEXCEPT { return component_family::type(); } @@ -280,7 +281,7 @@ public: * @return A pointer to the array of components of the given type. */ template - Component * raw() ENTT_NOEXCEPT { + inline Component * raw() ENTT_NOEXCEPT { return const_cast(const_cast(this)->raw()); } @@ -619,7 +620,7 @@ public: * @return A reference to the tag. */ template - Tag & get() ENTT_NOEXCEPT { + inline Tag & get() ENTT_NOEXCEPT { return const_cast(const_cast(this)->get()); } @@ -659,7 +660,7 @@ public: * @return A reference to the component owned by the entity. */ template - Component & get(entity_type entity) ENTT_NOEXCEPT { + inline Component & get(entity_type entity) ENTT_NOEXCEPT { return const_cast(const_cast(this)->get(entity)); } diff --git a/src/entt/entity/snapshot.hpp b/src/entt/entity/snapshot.hpp index 641a66b15..379d4709e 100644 --- a/src/entt/entity/snapshot.hpp +++ b/src/entt/entity/snapshot.hpp @@ -470,7 +470,7 @@ class ContinuousLoader final { each(archive, [&archive, member..., this](auto entity) { entity = restore(entity); - auto &tag = registry.template assign(entt::tag_t{}, entity); + auto &tag = registry.template assign(tag_t{}, entity); archive(tag); using accumulator_type = int[]; diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp index f80af2842..ff64bdba2 100644 --- a/src/entt/entity/sparse_set.hpp +++ b/src/entt/entity/sparse_set.hpp @@ -692,7 +692,7 @@ public: * @param entity A valid entity identifier. * @return The object associated to the entity. */ - object_type & get(entity_type entity) ENTT_NOEXCEPT { + inline object_type & get(entity_type entity) ENTT_NOEXCEPT { return const_cast(const_cast(this)->get(entity)); } diff --git a/src/entt/entity/utility.hpp b/src/entt/entity/utility.hpp index 5a1509f3e..6639d1dac 100644 --- a/src/entt/entity/utility.hpp +++ b/src/entt/entity/utility.hpp @@ -5,39 +5,19 @@ namespace entt { -/** - * @brief Tag class type. - * - * An empty class type used to disambiguate the overloads of some member - * functions. - */ +/*! @brief Tag class type used to disambiguate overloads. */ struct tag_t final {}; -/** - * @brief Persistent view type. - * - * An empty class type used to disambiguate the overloads of some member - * functions. - */ +/*! @brief Persistent view type used to disambiguate overloads. */ struct persistent_t final {}; -/** - * @brief Raw view type. - * - * An empty class type used to disambiguate the overloads of some member - * functions. - */ +/*! @brief Raw view type used to disambiguate overloads. */ struct raw_t final {}; -/** - * @brief Break type. - * - * An empty class type used to disambiguate the overloads of some member - * functions. - */ +/*! @brief Break type used to disambiguate overloads. */ struct break_t final {}; diff --git a/src/entt/entity/view.hpp b/src/entt/entity/view.hpp index e6bf29e2b..e5b49bb16 100644 --- a/src/entt/entity/view.hpp +++ b/src/entt/entity/view.hpp @@ -9,6 +9,7 @@ #include #include #include "../config/config.h" +#include "../core/ident.hpp" #include "entt_traits.hpp" #include "sparse_set.hpp" @@ -247,7 +248,7 @@ public: * @return The component assigned to the entity. */ template - Comp & get(entity_type entity) ENTT_NOEXCEPT { + inline Comp & get(entity_type entity) ENTT_NOEXCEPT { return const_cast(const_cast(this)->get(entity)); } @@ -339,9 +340,9 @@ public: * @param func A valid function object. */ template - void each(Func func) { - std::for_each(view.begin(), view.end(), [&func, this](const auto entity) { - func(entity, std::get &>(pools).get(entity)...); + inline void each(Func func) { + const_cast(this)->each([&func](entity_type entity, const Component &... component) { + func(entity, const_cast(component)...); }); } @@ -428,6 +429,28 @@ class View final { using pattern_type = std::tuple &...>; using traits_type = entt_traits; + template + struct ComponentList {}; + + template, typename = void> + struct SplitComponentListBy; + + template + struct SplitComponentListBy, ComponentList, std::enable_if_t::value>> { + using pre_type = ComponentList; + using post_type = ComponentList; + }; + + template + struct SplitComponentListBy, ComponentList, std::enable_if_t::value>> + : SplitComponentListBy, ComponentList> {}; + + template + using PreList = typename SplitComponentListBy>::pre_type; + + template + using PostList = typename SplitComponentListBy>::post_type; + class Iterator { using size_type = typename view_type::size_type; @@ -498,11 +521,32 @@ class View final { }; View(pool_type &... pools) ENTT_NOEXCEPT - : pools{pools...}, view{nullptr}, unchecked{} + : pools{pools...}, view{nullptr}, unchecked{}, idx{} { reset(); } + template + void each(Func func, ComponentList, ComponentList) const { + const auto extent = std::min({ std::get &>(pools).extent()... }); + auto &pool = std::get &>(pools); + + std::for_each(pool.view_type::cbegin(), pool.view_type::cend(), [&func, raw = pool.cbegin(), extent, this](const auto entity) mutable { + const auto sz = size_type(entity & traits_type::entity_mask); + + if(sz < extent) { + auto pos = unchecked.size(); + + for(; pos && unchecked[pos-1]->fast(entity); --pos); + + if(!pos) { + // avoided indirections due to the sparse set for the pivot + func(entity, std::get &>(pools).get(entity)..., *(raw)++, std::get &>(pools).get(entity)...); + } + } + }); + } + public: /*! @brief Input iterator type. */ using iterator_type = Iterator; @@ -663,7 +707,7 @@ public: * @return The component assigned to the entity. */ template - Comp & get(entity_type entity) ENTT_NOEXCEPT { + inline Comp & get(entity_type entity) ENTT_NOEXCEPT { return const_cast(const_cast(this)->get(entity)); } @@ -732,22 +776,11 @@ public: * @param func A valid function object. */ template - void each(Func func) const { - const auto extent = std::min({ std::get &>(pools).extent()... }); - - std::for_each(view->cbegin(), view->cend(), [&func, extent, this](const auto entity) { - const auto sz = size_type(entity & traits_type::entity_mask); - - if(sz < extent) { - auto pos = unchecked.size(); - - for(; pos && unchecked[pos-1]->fast(entity); --pos); - - if(!pos) { - func(entity, std::get &>(pools).get(entity)...); - } - } - }); + inline void each(Func func) const { + constexpr auto indexes = ident; + using accumulator_type = int[]; + accumulator_type accumulator = { (indexes.template get() == idx ? (each(std::move(func), PreList{}, PostList{}), 0) : 0)... }; + (void)accumulator; } /** @@ -767,7 +800,7 @@ public: * @param func A valid function object. */ template - void each(Func func) { + inline void each(Func func) { const_cast(this)->each([&func](entity_type entity, const Component &... component) { func(entity, const_cast(component)...); }); @@ -786,18 +819,18 @@ public: void reset() { using accumulator_type = size_type[]; size_type sz = std::max({ std::get &>(pools).size()... }) + std::size_t{1}; - size_type pos{}; + size_type next{}; auto probe = [this](auto sz, const auto &pool) { return pool.size() < sz ? (view = &pool, pool.size()) : sz; }; - auto filter = [this](auto pos, const auto &pool) { - return (view != &pool) ? (unchecked[pos++] = &pool, pos) : pos; + auto filter = [this](auto next, const auto &pool) { + return (view == &pool) ? (idx = next) : (unchecked[next++] = &pool, next); }; accumulator_type probing = { (sz = probe(sz, std::get &>(pools)))... }; - accumulator_type filtering = { (pos = filter(pos, std::get &>(pools)))... }; + accumulator_type filtering = { (next = filter(next, std::get &>(pools)))... }; (void)filtering; (void)probing; @@ -807,6 +840,7 @@ private: const pattern_type pools; const view_type *view; unchecked_type unchecked; + size_type idx; }; @@ -915,7 +949,7 @@ public: * * @return A pointer to the array of components. */ - raw_type * raw() ENTT_NOEXCEPT { + inline raw_type * raw() ENTT_NOEXCEPT { return const_cast(const_cast(this)->raw()); } @@ -1053,7 +1087,7 @@ public: * @param entity A valid entity identifier. * @return The component assigned to the entity. */ - Component & get(entity_type entity) ENTT_NOEXCEPT { + inline Component & get(entity_type entity) ENTT_NOEXCEPT { return const_cast(const_cast(this)->get(entity)); } @@ -1074,8 +1108,8 @@ public: */ template void each(Func func) const { - std::for_each(pool.view_type::cbegin(), pool.view_type::cend(), [&func, this](const auto entity) { - func(entity, pool.get(entity)); + std::for_each(pool.view_type::cbegin(), pool.view_type::cend(), [&func, raw = pool.cbegin()](const auto entity) mutable { + func(entity, *(raw++)); }); } @@ -1095,9 +1129,9 @@ public: * @param func A valid function object. */ template - void each(Func func) { - std::for_each(pool.view_type::begin(), pool.view_type::end(), [&func, this](const auto entity) { - func(entity, pool.get(entity)); + inline void each(Func func) { + const_cast(this)->each([&func](entity_type entity, const Component &component) { + func(entity, const_cast(component)); }); } @@ -1211,7 +1245,7 @@ public: * * @return A pointer to the array of components. */ - raw_type * raw() ENTT_NOEXCEPT { + inline raw_type * raw() ENTT_NOEXCEPT { return const_cast(const_cast(this)->raw()); } diff --git a/test/benchmark/benchmark.cpp b/test/benchmark/benchmark.cpp index 55f562760..ee449b0ae 100644 --- a/test/benchmark/benchmark.cpp +++ b/test/benchmark/benchmark.cpp @@ -77,13 +77,11 @@ TEST(Benchmark, IterateCreateDeleteSingleComponent) { registry.assign(entity); } - view.each([&](auto entity, auto &position) { - (void)position; - + for(auto entity: view) { if(rand() % 2 == 0) { registry.destroy(entity); } - }); + } } timer.elapsed(); @@ -99,9 +97,16 @@ TEST(Benchmark, IterateSingleComponent1M) { registry.assign(entity); } - Timer timer; - registry.view().each([](auto, auto &) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &) {}); + test([](auto, auto &position) { + position.x = {}; + }); } TEST(Benchmark, IterateSingleComponentRaw1M) { @@ -114,11 +119,16 @@ TEST(Benchmark, IterateSingleComponentRaw1M) { registry.assign(entity); } - Timer timer; - for(auto &&component: registry.view(entt::raw_t{})) { - (void)component; - } - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view(entt::raw_t{}).each(func); + timer.elapsed(); + }; + + test([](const auto &) {}); + test([](auto &position) { + position.x = {}; + }); } TEST(Benchmark, IterateTwoComponents1M) { @@ -132,9 +142,17 @@ TEST(Benchmark, IterateTwoComponents1M) { registry.assign(entity); } - Timer timer; - registry.view().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity) { + position.x = {}; + velocity.x = {}; + }); } TEST(Benchmark, IterateTwoComponents1MHalf) { @@ -151,9 +169,17 @@ TEST(Benchmark, IterateTwoComponents1MHalf) { } } - Timer timer; - registry.view().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity) { + position.x = {}; + velocity.x = {}; + }); } TEST(Benchmark, IterateTwoComponents1MOne) { @@ -170,9 +196,17 @@ TEST(Benchmark, IterateTwoComponents1MOne) { } } - Timer timer; - registry.view().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity) { + position.x = {}; + velocity.x = {}; + }); } TEST(Benchmark, IterateTwoComponentsPersistent1M) { @@ -187,9 +221,17 @@ TEST(Benchmark, IterateTwoComponentsPersistent1M) { registry.assign(entity); } - Timer timer; - registry.view(entt::persistent_t{}).each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view(entt::persistent_t{}).each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity) { + position.x = {}; + velocity.x = {}; + }); } TEST(Benchmark, IterateFiveComponents1M) { @@ -206,9 +248,20 @@ TEST(Benchmark, IterateFiveComponents1M) { registry.assign>(entity); } - Timer timer; - registry.view, Comp<2>, Comp<3>>().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>>().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + }); } TEST(Benchmark, IterateFiveComponents1MHalf) { @@ -228,9 +281,20 @@ TEST(Benchmark, IterateFiveComponents1MHalf) { } } - Timer timer; - registry.view, Comp<2>, Comp<3>>().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>>().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + }); } TEST(Benchmark, IterateFiveComponents1MOne) { @@ -250,9 +314,20 @@ TEST(Benchmark, IterateFiveComponents1MOne) { } } - Timer timer; - registry.view, Comp<2>, Comp<3>>().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>>().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + }); } TEST(Benchmark, IterateFiveComponentsPersistent1M) { @@ -270,9 +345,20 @@ TEST(Benchmark, IterateFiveComponentsPersistent1M) { registry.assign>(entity); } - Timer timer; - registry.view, Comp<2>, Comp<3>>(entt::persistent_t{}).each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>>(entt::persistent_t{}).each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + }); } TEST(Benchmark, IterateTenComponents1M) { @@ -294,9 +380,25 @@ TEST(Benchmark, IterateTenComponents1M) { 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(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3, auto &comp4, auto &comp5, auto &comp6, auto &comp7, auto &comp8) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + comp4.x = {}; + comp5.x = {}; + comp6.x = {}; + comp7.x = {}; + comp8.x = {}; + }); } TEST(Benchmark, IterateTenComponents1MHalf) { @@ -321,9 +423,25 @@ TEST(Benchmark, IterateTenComponents1MHalf) { } } - Timer timer; - registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each(func); + timer.elapsed(); + }; + + test([](auto, auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3, auto &comp4, auto &comp5, auto &comp6, auto &comp7, auto &comp8) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + comp4.x = {}; + comp5.x = {}; + comp6.x = {}; + comp7.x = {}; + comp8.x = {}; + }); } TEST(Benchmark, IterateTenComponents1MOne) { @@ -348,9 +466,25 @@ TEST(Benchmark, IterateTenComponents1MOne) { } } - Timer timer; - registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3, auto &comp4, auto &comp5, auto &comp6, auto &comp7, auto &comp8) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + comp4.x = {}; + comp5.x = {}; + comp6.x = {}; + comp7.x = {}; + comp8.x = {}; + }); } TEST(Benchmark, IterateTenComponentsPersistent1M) { @@ -373,9 +507,25 @@ TEST(Benchmark, IterateTenComponentsPersistent1M) { registry.assign>(entity); } - Timer timer; - registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(entt::persistent_t{}).each([](auto, auto &...) {}); - timer.elapsed(); + auto test = [®istry](auto func) { + Timer timer; + registry.view, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(entt::persistent_t{}).each(func); + timer.elapsed(); + }; + + test([](auto, const auto &...) {}); + test([](auto, auto &position, auto &velocity, auto &comp1, auto &comp2, auto &comp3, auto &comp4, auto &comp5, auto &comp6, auto &comp7, auto &comp8) { + position.x = {}; + velocity.x = {}; + comp1.x = {}; + comp2.x = {}; + comp3.x = {}; + comp4.x = {}; + comp5.x = {}; + comp6.x = {}; + comp7.x = {}; + comp8.x = {}; + }); } TEST(Benchmark, SortSingle) {