Browse Source

performance improvements

Michele Caini 8 years ago
parent
commit
169cb66276
4 changed files with 116 additions and 114 deletions
  1. 13 21
      README.md
  2. 35 4
      src/component_pool.hpp
  3. 58 79
      src/registry.hpp
  4. 10 10
      test/component_pool.cpp

+ 13 - 21
README.md

@@ -209,7 +209,7 @@ Once you have created a registry, the followings are the exposed member function
 * `reset()`: resets the pool and destroys all the entities and their components.
 * `view<Components...>()`: gets a view of the entities that have the given components (see below for further details).
 
-Note that entities are numbers and nothing more. They are not classes and they have no memeber functions at all.
+Note that entities are numbers and nothing more. They are not classes and they have no member functions at all.
 
 #### The View
 
@@ -229,7 +229,7 @@ for(auto entity: view) {
 ```
 
 Iterators are extremely poor, they are meant exclusively to be used to iterate over a set of entities.<br/>
-Exposed member functions are:
+Guaranteed exposed member functions are:
 
 * `operator++()`
 * `operator++(int)`
@@ -250,24 +250,10 @@ All the views can be used more than once. They return newly created and correctl
 I'd suggest not to store them anywhere and to invoke the `Registry::view` member function at each iteration to get a properly
 initialized view over which to iterate.
 
-**Note**: An important feature (usually missed by other well known ECS) is that users can create and destroy entities, as
-well as assign or remove components while iterating and neither the views nor the iterators will be invalidated.
+**Note**: If underlying sets are modified, iterators are invalidated and using them is undefined behaviour.<br/>
+Do not try to assign or remove components on which you are iterating to entities. There are no guarantees.
 
-There are a few exceptions to the rule:
-
-* Trying to access a destroyed entity through an iterator that hasn't been advanced results in an undefined behavior.
-* Destroying an entity that isn't the one returned by the iterator in use results in an undefined behavior.
-* Removing a component from an entity that isn't the one returned by the iterator in use results in an undefined behavior.
-
-In other therms, users can freely interact with the registry and keep views and iterators consistent as long as:
-
-* They create new entities with their set of components or assign components of any type to an already existent entity.
-* They destroy the current entity (that is the one returned by the iterator in use) or remove its components.
-
-In any other case the behavior is undefined and additions and deletions should be managed externally in a batch or whatever.
-As an example, whenever there exists a parent-child relationship, one can incurr in the problem above mentioned.
-
-Note also that iterators aren't thread safe. Do no try to iterate over a set of components and modify them concurrently.
+**Note**: Iterators aren't thread safe. Do no try to iterate over a set of components and modify them concurrently.
 That being said, as long as a thread iterates over the entities that have the component `X` or assign and removes
 that component from a set of entities and another thread does something similar with components `Y` and `Z`, it shouldn't be a
 problem at all.<br/>
@@ -293,7 +279,10 @@ A custom pool should expose at least the following member functions:
 * `bool empty() const noexcept;`
 * `size_type capacity() const noexcept;`
 * `size_type size() const noexcept;`
-* `const entity_type * entities() const noexcept;`
+* `iterator_type begin() noexcept;`
+* `const_iterator_type begin() const noexcept;`
+* `iterator_type end() noexcept;`
+* `const_iterator_type end() const noexcept;`
 * `bool has(entity_type entity) const noexcept;`
 * `const component_type & get(entity_type entity) const noexcept;`
 * `component_type & get(entity_type entity) noexcept;`
@@ -322,7 +311,10 @@ A generic pool should expose at least the following memeber functions:
 * `template<typename Component> bool empty() const noexcept;`
 * `template<typename Component> size_type capacity() const noexcept;`
 * `template<typename Component> size_type size() const noexcept;`
-* `template<typename Component> const entity_type * entities() const noexcept;`
+* `template<typename Component> iterator_type begin() noexcept;`
+* `template<typename Component> const_iterator_type begin() const noexcept;`
+* `template<typename Component> iterator_type end() noexcept;`
+* `template<typename Component> const_iterator_type end() const noexcept;`
 * `template<typename Component> bool has(entity_type entity) const noexcept;`
 * `template<typename Component> const Comp & get(entity_type entity) const noexcept;`
 * `template<typename Component> Comp & get(entity_type entity) noexcept;`

+ 35 - 4
src/component_pool.hpp

@@ -21,6 +21,8 @@ public:
     using entity_type = Entity;
     using pos_type = entity_type;
     using size_type = typename std::vector<component_type>::size_type;
+    using iterator_type = typename std::vector<entity_type>::iterator;
+    using const_iterator_type = typename std::vector<entity_type>::const_iterator;
 
 private:
     inline bool valid(entity_type entity) const noexcept {
@@ -53,8 +55,20 @@ public:
         return data.size();
     }
 
-    const entity_type * entities() const noexcept {
-        return direct.data();
+    iterator_type begin() noexcept {
+        return direct.begin();
+    }
+
+    const_iterator_type begin() const noexcept {
+        return direct.begin();
+    }
+
+    iterator_type end() noexcept {
+        return direct.end();
+    }
+
+    const_iterator_type end() const noexcept {
+        return direct.end();
     }
 
     bool has(entity_type entity) const noexcept {
@@ -122,6 +136,8 @@ public:
     using entity_type = typename Pool<Component>::entity_type;
     using pos_type = typename Pool<Component>::pos_type;
     using size_type = typename Pool<Component>::size_type;
+    using iterator_type = typename Pool<Component>::iterator_type;
+    using const_iterator_type = typename Pool<Component>::const_iterator_type;
 
     explicit ComponentPool(size_type dim = 4098) noexcept
 #ifdef _MSC_VER
@@ -155,8 +171,23 @@ public:
     }
 
     template<typename Comp>
-    const entity_type * entities() const noexcept {
-        return Pool<Comp>::entities();
+    iterator_type begin() noexcept {
+        return Pool<Comp>::begin();
+    }
+
+    template<typename Comp>
+    const_iterator_type begin() const noexcept {
+        return Pool<Comp>::begin();
+    }
+
+    template<typename Comp>
+    iterator_type end() noexcept {
+        return Pool<Comp>::end();
+    }
+
+    template<typename Comp>
+    const_iterator_type end() const noexcept {
+        return Pool<Comp>::end();
     }
 
     template<typename Comp>

+ 58 - 79
src/registry.hpp

@@ -25,17 +25,19 @@ class View<Pool<Entity, Components...>, Type, Types...> final {
     using pool_type = Pool<Entity, Components...>;
     using entity_type = typename pool_type::entity_type;
     using mask_type = std::bitset<sizeof...(Components)+1>;
+    using underlying_iterator_type = typename pool_type::const_iterator_type;
 
     class ViewIterator;
 
 public:
     using iterator_type = ViewIterator;
+    using const_iterator_type = iterator_type;
     using size_type = typename pool_type::size_type;
 
 private:
     class ViewIterator {
         inline bool valid() const noexcept {
-            return ((mask[entities[pos]] & bitmask) == bitmask);
+            return ((mask[*begin] & bitmask) == bitmask);
         }
 
     public:
@@ -45,25 +47,26 @@ private:
         using pointer = entity_type *;
         using iterator_category = std::input_iterator_tag;
 
-        ViewIterator(const mask_type &bitmask, const entity_type *entities, const mask_type *mask, size_type pos, size_type last) noexcept
-            : bitmask{bitmask}, entities{entities}, mask{mask}, pos{pos}, last{last}
+        ViewIterator(underlying_iterator_type begin, underlying_iterator_type end, const mask_type &bitmask, const mask_type *mask) noexcept
+            : begin{begin}, end{end}, bitmask{bitmask}, mask{mask}
         {
-            while(this->pos != last && !valid()) { ++this->pos; }
+            if(begin != end && !valid()) {
+                ++(*this);
+            }
         }
 
         ViewIterator & operator++() noexcept {
-            ++pos;
-            while(pos != last && !valid()) { ++pos; }
+            while(++begin != end && !valid());
             return *this;
         }
 
         ViewIterator operator++(int) noexcept {
             ViewIterator orig = *this;
-            return this->operator++(), orig;
+            return ++(*this), orig;
         }
 
         bool operator==(const ViewIterator &other) const noexcept {
-            return other.pos == pos && other.entities == entities;
+            return other.begin == begin;
         }
 
         bool operator!=(const ViewIterator &other) const noexcept {
@@ -71,62 +74,72 @@ private:
         }
 
         value_type operator*() const noexcept {
-            return entities[pos];
+            return *begin;
         }
 
     private:
+        underlying_iterator_type begin;
+        underlying_iterator_type end;
         const mask_type bitmask;
-        const entity_type *entities;
         const mask_type *mask;
-        size_type pos;
-        size_type last;
     };
 
     template<typename Comp>
-    void prefer() noexcept {
+    void prefer(size_type &size) noexcept {
         auto sz = pool.template size<Comp>();
 
         if(sz < size) {
-            entities = pool.template entities<Comp>();
+            from = pool.template begin<Type>();
+            to = pool.template end<Type>();
             size = sz;
         }
     }
 
 public:
-    explicit View(const pool_type &pool, const mask_type *mask) noexcept
-        : entities{pool.template entities<Type>()},
+    explicit View(pool_type &pool, const mask_type *mask) noexcept
+        : from{pool.template begin<Type>()},
+          to{pool.template end<Type>()},
           pool{pool},
-          mask{mask},
-          size{pool.template size<Type>()}
+          mask{mask}
     {
         using accumulator_type = int[];
+        size_type size = pool.template size<Type>();
         bitmask.set(ident<Components...>.template get<Type>());
         accumulator_type types = { 0, (bitmask.set(ident<Components...>.template get<Types>()), 0)... };
-        accumulator_type pref = { 0, (prefer<Types>(), 0)... };
+        accumulator_type pref = { 0, (prefer<Types>(size), 0)... };
         (void)types, (void)pref;
     }
 
-    iterator_type begin() const noexcept {
-        return ViewIterator{bitmask, entities, mask, 0, size};
+    const_iterator_type begin() const noexcept {
+        return ViewIterator{from, to, bitmask, mask};
+    }
+
+    iterator_type begin() noexcept {
+        return const_cast<const View *>(this)->begin();
     }
 
-    iterator_type end() const noexcept {
-        return ViewIterator{bitmask, entities, mask, size, size};
+    const_iterator_type end() const noexcept {
+        return ViewIterator{to, to, bitmask, mask};
+    }
+
+    iterator_type end() noexcept {
+        return const_cast<const View *>(this)->end();
     }
 
     void reset() noexcept {
         using accumulator_type = int[];
-        entities = pool.template entities<Type>();
-        size = pool.template size<Type>();
-        accumulator_type accumulator = { 0, (prefer<Types>(), 0)... };
+        from = pool.template begin<Type>();
+        to = pool.template end<Type>();
+        size_type size = pool.template size<Type>();
+        accumulator_type accumulator = { 0, (prefer<Types>(size), 0)... };
         (void)accumulator;
     }
 
 private:
-    const entity_type *entities;
-    const pool_type &pool;
+    underlying_iterator_type from;
+    underlying_iterator_type to;
+    pool_type &pool;
     const mask_type *mask;
-    size_type size;
     mask_type bitmask;
 };
 
@@ -134,64 +147,30 @@ private:
 template<template<typename...> class Pool, typename Entity, typename... Components, typename Type>
 class View<Pool<Entity, Components...>, Type> final {
     using pool_type = Pool<Entity, Components...>;
-    using entity_type = typename pool_type::entity_type;
-
-    struct ViewIterator;
 
 public:
     using size_type = typename pool_type::size_type;
-    using iterator_type = ViewIterator;
-
-private:
-    struct ViewIterator {
-        using value_type = entity_type;
-        using difference_type = std::ptrdiff_t;
-        using reference = entity_type &;
-        using pointer = entity_type *;
-        using iterator_category = std::input_iterator_tag;
-
-        ViewIterator(const entity_type *entities, size_type pos) noexcept
-            : entities{entities}, pos{pos}
-        {}
-
-        ViewIterator & operator++() noexcept {
-            ++pos;
-            return *this;
-        }
-
-        ViewIterator operator++(int) noexcept {
-            ViewIterator orig = *this;
-            return this->operator++(), orig;
-        }
-
-        bool operator==(const ViewIterator &other) const noexcept {
-            return other.pos == pos && other.entities == entities;
-        }
+    using iterator_type = typename pool_type::const_iterator_type;
+    using const_iterator_type = iterator_type;
 
-        bool operator!=(const ViewIterator &other) const noexcept {
-            return !(*this == other);
-        }
-
-        value_type operator*() const noexcept {
-            return entities[pos];
-        }
-
-    private:
-        const entity_type *entities;
-        size_type pos;
-    };
-
-public:
-    explicit View(const pool_type &pool) noexcept
+    explicit View(pool_type &pool) noexcept
         : pool{pool}
     {}
 
-    iterator_type begin() const noexcept {
-        return ViewIterator{pool.template entities<Type>(), 0};
+    const_iterator_type begin() const noexcept {
+        return pool.template begin<Type>();
+    }
+
+    iterator_type begin() noexcept {
+        return const_cast<const View *>(this)->begin();
+    }
+
+    const_iterator_type end() const noexcept {
+        return pool.template end<Type>();
     }
 
-    iterator_type end() const noexcept {
-        return ViewIterator{pool.template entities<Type>(), pool.template size<Type>()};
+    iterator_type end() noexcept {
+        return const_cast<const View *>(this)->end();
     }
 
     size_type size() const noexcept {
@@ -199,7 +178,7 @@ public:
     }
 
 private:
-    const pool_type &pool;
+    pool_type &pool;
 };
 
 

+ 10 - 10
test/component_pool.cpp

@@ -13,8 +13,8 @@ TEST(ComponentPool, Functionalities) {
     ASSERT_EQ(pool.capacity<double>(), pool_type::size_type{0});
     ASSERT_EQ(pool.size<int>(), pool_type::size_type{0});
     ASSERT_EQ(pool.size<double>(), pool_type::size_type{0});
-    ASSERT_EQ(pool.entities<int>(), pool.entities<int>() + pool.size<int>());
-    ASSERT_EQ(pool.entities<double>(), pool.entities<double>() + pool.size<double>());
+    ASSERT_EQ(pool.begin<int>(), pool.end<int>());
+    ASSERT_EQ(pool.begin<double>(), pool.end<double>());
     ASSERT_FALSE(pool.has<int>(0));
     ASSERT_FALSE(pool.has<double>(0));
 }
@@ -126,7 +126,7 @@ TEST(ComponentPool, HasGet) {
     ASSERT_NO_THROW(pool.destroy<int>(0));
 }
 
-TEST(ComponentPool, EntitiesReset) {
+TEST(ComponentPool, BeginEndReset) {
     using pool_type = entt::ComponentPool<std::uint8_t, int, char>;
 
     pool_type pool{2};
@@ -137,17 +137,17 @@ TEST(ComponentPool, EntitiesReset) {
     ASSERT_EQ(pool.construct<int>(1, 1), 1);
 
     ASSERT_EQ(pool.size<int>(), decltype(pool.size<int>()){4});
-    ASSERT_EQ(pool.entities<int>()[0], typename pool_type::entity_type{0});
-    ASSERT_EQ(pool.entities<int>()[1], typename pool_type::entity_type{2});
-    ASSERT_EQ(pool.entities<int>()[2], typename pool_type::entity_type{3});
-    ASSERT_EQ(pool.entities<int>()[3], typename pool_type::entity_type{1});
+    ASSERT_EQ(*(pool.begin<int>()+0), typename pool_type::entity_type{0});
+    ASSERT_EQ(*(pool.begin<int>()+1), typename pool_type::entity_type{2});
+    ASSERT_EQ(*(pool.begin<int>()+2), typename pool_type::entity_type{3});
+    ASSERT_EQ(*(pool.begin<int>()+3), typename pool_type::entity_type{1});
 
     pool.destroy<int>(2);
 
     ASSERT_EQ(pool.size<int>(), decltype(pool.size<int>()){3});
-    ASSERT_EQ(pool.entities<int>()[0], typename pool_type::entity_type{0});
-    ASSERT_EQ(pool.entities<int>()[1], typename pool_type::entity_type{1});
-    ASSERT_EQ(pool.entities<int>()[2], typename pool_type::entity_type{3});
+    ASSERT_EQ(*(pool.begin<int>()+0), typename pool_type::entity_type{0});
+    ASSERT_EQ(*(pool.begin<int>()+1), typename pool_type::entity_type{1});
+    ASSERT_EQ(*(pool.begin<int>()+2), typename pool_type::entity_type{3});
 
     ASSERT_EQ(pool.construct<char>(0, 'c'), 'c');