Michele Caini 7 лет назад
Родитель
Сommit
08dc2fcf33

+ 14 - 5
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

+ 2 - 1
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

+ 3 - 3
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<typename Tag>
-    Tag & get(tag_t) ENTT_NOEXCEPT {
+    inline Tag & get(tag_t) ENTT_NOEXCEPT {
         return const_cast<Tag &>(const_cast<const Actor *>(this)->get<Tag>(tag_t{}));
     }
 
@@ -163,7 +163,7 @@ struct Actor {
      * @return A reference to the instance of the component owned by the actor.
      */
     template<typename Component>
-    Component & get() ENTT_NOEXCEPT {
+    inline Component & get() ENTT_NOEXCEPT {
         return const_cast<Component &>(const_cast<const Actor *>(this)->get<Component>());
     }
 
@@ -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<registry_type &>(const_cast<const Actor *>(this)->registry());
     }
 

+ 11 - 10
src/entt/entity/registry.hpp

@@ -44,14 +44,15 @@ class Registry {
 
     template<typename... Component>
     static void creating(Registry &registry, Entity entity) {
-        const auto ttype = handler_family::type<Component...>();
-        return registry.has<Component...>(entity) ? registry.handlers[ttype]->construct(entity) : void();
+        if(registry.has<Component...>(entity)) {
+            registry.handlers[handler_family::type<Component...>()]->construct(entity);
+        }
     }
 
     template<typename... Component>
     static void destroying(Registry &registry, Entity entity) {
-        auto &handler = registry.handlers[handler_family::type<Component...>()];
-        return handler->has(entity) ? handler->destroy(entity) : void();
+        auto &handler = *registry.handlers[handler_family::type<Component...>()];
+        return handler.has(entity) ? handler.destroy(entity) : void();
     }
 
     struct Attachee {
@@ -85,7 +86,7 @@ class Registry {
     }
 
     template<typename Component>
-    SparseSet<Entity, Component> & pool() ENTT_NOEXCEPT {
+    inline SparseSet<Entity, Component> & pool() ENTT_NOEXCEPT {
         return const_cast<SparseSet<Entity, Component> &>(const_cast<const Registry *>(this)->pool<Component>());
     }
 
@@ -153,7 +154,7 @@ public:
      * @return Runtime numeric identifier of the given type of tag.
      */
     template<typename Tag>
-    tag_type type(tag_t) const ENTT_NOEXCEPT {
+    static tag_type type(tag_t) ENTT_NOEXCEPT {
         return tag_family::type<Tag>();
     }
 
@@ -170,7 +171,7 @@ public:
      * @return Runtime numeric identifier of the given type of component.
      */
     template<typename Component>
-    component_type type() const ENTT_NOEXCEPT {
+    static component_type type() ENTT_NOEXCEPT {
         return component_family::type<Component>();
     }
 
@@ -280,7 +281,7 @@ public:
      * @return A pointer to the array of components of the given type.
      */
     template<typename Component>
-    Component * raw() ENTT_NOEXCEPT {
+    inline Component * raw() ENTT_NOEXCEPT {
         return const_cast<Component *>(const_cast<const Registry *>(this)->raw<Component>());
     }
 
@@ -619,7 +620,7 @@ public:
      * @return A reference to the tag.
      */
     template<typename Tag>
-    Tag & get() ENTT_NOEXCEPT {
+    inline Tag & get() ENTT_NOEXCEPT {
         return const_cast<Tag &>(const_cast<const Registry *>(this)->get<Tag>());
     }
 
@@ -659,7 +660,7 @@ public:
      * @return A reference to the component owned by the entity.
      */
     template<typename Component>
-    Component & get(entity_type entity) ENTT_NOEXCEPT {
+    inline Component & get(entity_type entity) ENTT_NOEXCEPT {
         return const_cast<Component &>(const_cast<const Registry *>(this)->get<Component>(entity));
     }
 

+ 1 - 1
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<Tag>(entt::tag_t{}, entity);
+            auto &tag = registry.template assign<Tag>(tag_t{}, entity);
             archive(tag);
 
             using accumulator_type = int[];

+ 1 - 1
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<object_type &>(const_cast<const SparseSet *>(this)->get(entity));
     }
 

+ 4 - 24
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 {};
 
 

+ 69 - 35
src/entt/entity/view.hpp

@@ -9,6 +9,7 @@
 #include <algorithm>
 #include <type_traits>
 #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<typename Comp>
-    Comp & get(entity_type entity) ENTT_NOEXCEPT {
+    inline Comp & get(entity_type entity) ENTT_NOEXCEPT {
         return const_cast<Comp &>(const_cast<const PersistentView *>(this)->get<Comp>(entity));
     }
 
@@ -339,9 +340,9 @@ public:
      * @param func A valid function object.
      */
     template<typename Func>
-    void each(Func func) {
-        std::for_each(view.begin(), view.end(), [&func, this](const auto entity) {
-            func(entity, std::get<pool_type<Component> &>(pools).get(entity)...);
+    inline void each(Func func) {
+        const_cast<const PersistentView *>(this)->each([&func](entity_type entity, const Component &... component) {
+            func(entity, const_cast<Component &>(component)...);
         });
     }
 
@@ -428,6 +429,28 @@ class View final {
     using pattern_type = std::tuple<pool_type<Component> &...>;
     using traits_type = entt_traits<Entity>;
 
+    template<typename...>
+    struct ComponentList {};
+
+    template<typename, typename, typename = ComponentList<>, typename = void>
+    struct SplitComponentListBy;
+
+    template<typename Comp, typename Next, typename... Other, typename... Types>
+    struct SplitComponentListBy<Comp, ComponentList<Next, Other...>, ComponentList<Types...>, std::enable_if_t<std::is_same<Comp, Next>::value>> {
+        using pre_type = ComponentList<Types...>;
+        using post_type = ComponentList<Other...>;
+    };
+
+    template<typename Comp, typename Next, typename... Other, typename... Types>
+    struct SplitComponentListBy<Comp, ComponentList<Next, Other...>, ComponentList<Types...>, std::enable_if_t<!std::is_same<Comp, Next>::value>>
+            : SplitComponentListBy<Comp, ComponentList<Other...>, ComponentList<Types..., Next>> {};
+
+    template<typename Comp>
+    using PreList = typename SplitComponentListBy<Comp, ComponentList<Component...>>::pre_type;
+
+    template<typename Comp>
+    using PostList = typename SplitComponentListBy<Comp, ComponentList<Component...>>::post_type;
+
     class Iterator {
         using size_type = typename view_type::size_type;
 
@@ -498,11 +521,32 @@ class View final {
     };
 
     View(pool_type<Component> &... pools) ENTT_NOEXCEPT
-        : pools{pools...}, view{nullptr}, unchecked{}
+        : pools{pools...}, view{nullptr}, unchecked{}, idx{}
     {
         reset();
     }
 
+    template<typename Comp, typename... Pre, typename... Post, typename Func>
+    void each(Func func, ComponentList<Pre...>, ComponentList<Post...>) const {
+        const auto extent = std::min({ std::get<pool_type<Component> &>(pools).extent()... });
+        auto &pool = std::get<pool_type<Comp> &>(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<pool_type<Pre> &>(pools).get(entity)..., *(raw)++, std::get<pool_type<Post> &>(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<typename Comp>
-    Comp & get(entity_type entity) ENTT_NOEXCEPT {
+    inline Comp & get(entity_type entity) ENTT_NOEXCEPT {
         return const_cast<Comp &>(const_cast<const View *>(this)->get<Comp>(entity));
     }
 
@@ -732,22 +776,11 @@ public:
      * @param func A valid function object.
      */
     template<typename Func>
-    void each(Func func) const {
-        const auto extent = std::min({ std::get<pool_type<Component> &>(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<pool_type<Component> &>(pools).get(entity)...);
-                }
-            }
-        });
+    inline void each(Func func) const {
+        constexpr auto indexes = ident<Component...>;
+        using accumulator_type = int[];
+        accumulator_type accumulator = { (indexes.template get<Component>() == idx ? (each<Component>(std::move(func), PreList<Component>{}, PostList<Component>{}), 0) : 0)... };
+        (void)accumulator;
     }
 
     /**
@@ -767,7 +800,7 @@ public:
      * @param func A valid function object.
      */
     template<typename Func>
-    void each(Func func) {
+    inline void each(Func func) {
         const_cast<const View *>(this)->each([&func](entity_type entity, const Component &... component) {
             func(entity, const_cast<Component &>(component)...);
         });
@@ -786,18 +819,18 @@ public:
     void reset() {
         using accumulator_type = size_type[];
         size_type sz = std::max({ std::get<pool_type<Component> &>(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<pool_type<Component> &>(pools)))... };
-        accumulator_type filtering = { (pos = filter(pos, std::get<pool_type<Component> &>(pools)))... };
+        accumulator_type filtering = { (next = filter(next, std::get<pool_type<Component> &>(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<raw_type *>(const_cast<const View *>(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<Component &>(const_cast<const View *>(this)->get(entity));
     }
 
@@ -1074,8 +1108,8 @@ public:
      */
     template<typename Func>
     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<typename Func>
-    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<const View *>(this)->each([&func](entity_type entity, const Component &component) {
+            func(entity, const_cast<Component &>(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<raw_type *>(const_cast<const RawView *>(this)->raw());
     }
 

+ 198 - 48
test/benchmark/benchmark.cpp

@@ -77,13 +77,11 @@ TEST(Benchmark, IterateCreateDeleteSingleComponent) {
             registry.assign<Position>(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<Position>(entity);
     }
 
-    Timer timer;
-    registry.view<Position>().each([](auto, auto &) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position>().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<Position>(entity);
     }
 
-    Timer timer;
-    for(auto &&component: registry.view<Position>(entt::raw_t{})) {
-        (void)component;
-    }
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position>(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<Velocity>(entity);
     }
 
-    Timer timer;
-    registry.view<Position, Velocity>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity>().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<Position, Velocity>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity>().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<Position, Velocity>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity>().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<Velocity>(entity);
     }
 
-    Timer timer;
-    registry.view<Position, Velocity>(entt::persistent_t{}).each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity>(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<Comp<3>>(entity);
     }
 
-    Timer timer;
-    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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<Comp<3>>(entity);
     }
 
-    Timer timer;
-    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>>(entt::persistent_t{}).each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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<Comp<8>>(entity);
     }
 
-    Timer timer;
-    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>().each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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<Comp<8>>(entity);
     }
 
-    Timer timer;
-    registry.view<Position, Velocity, Comp<1>, Comp<2>, Comp<3>, Comp<4>, Comp<5>, Comp<6>, Comp<7>, Comp<8>>(entt::persistent_t{}).each([](auto, auto &...) {});
-    timer.elapsed();
+    auto test = [&registry](auto func) {
+        Timer timer;
+        registry.view<Position, Velocity, Comp<1>, 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) {