Explorar el Código

Experimental (#8)

performance improvements
Michele Caini hace 8 años
padre
commit
60838c2b62
Se han modificado 5 ficheros con 208 adiciones y 136 borrados
  1. 1 2
      CMakeLists.txt
  2. 0 18
      README.md
  3. 61 68
      src/registry.hpp
  4. 140 0
      test/benchmark.cpp
  5. 6 48
      test/registry.cpp

+ 1 - 2
CMakeLists.txt

@@ -40,9 +40,8 @@ set(CMAKE_CXX_STANDARD 14)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-undefined")
 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pedantic -Wall -Wconversion")
-# set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} "-Wextra -Weffc++")
 set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -g -DDEBUG")
-set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3 -DRELEASE")
+set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O2 -DRELEASE")
 
 #
 # CMake configuration

+ 0 - 18
README.md

@@ -74,12 +74,6 @@ int main() {
         else { ecs.destroy(entity); }
     }
 
-    std::cout << "filtered component view" << std::endl;
-
-    for(auto entity: ecs.view<Position>().exclude<Velocity>()) {
-        std::cout << (ecs.has<Position>(entity)) << "/" << (ecs.has<Velocity>(entity)) << std::endl;
-    }
-
     ecs.reset();
 }
 ```
@@ -223,7 +217,6 @@ There are three different kinds of view, each one with a slighlty different inte
 
 * The _single component view_.
 * The _multi component view_.
-* The _filtered view_.
 
 All of them are iterable. In other terms they have `begin` and `end` member functions that are suitable for a range-based for loop:
 
@@ -252,17 +245,6 @@ The multi component view has an additional member function:
 
 * `reset()`: reorganizes internal data so as to further create optimized iterators (use it whenever the data within the registry are known to be changed).
 
-A filtered view is nothing more than a multi component view with an additional set of components that act as filters.<br/>
-Users can create filtered views either from a single component view or from a multi component view by means of the `exclude` member function:
-
-```cpp
-auto view = registry.view<Position>().exclude<Velocity>();
-
-for(auto entity: view) {
-    // do whatever you want with your entities
-}
-```
-
 All the views can be used more than once. They return newly created and correctly initialized iterators whenever
 `begin` or `end` is invoked. Anyway views and iterators are tiny objects and the time to construct them can be safely ignored.
 I'd suggest not to store them anywhere and to invoke the `Registry::view` member function at each iteration to get a properly

+ 61 - 68
src/registry.hpp

@@ -2,13 +2,13 @@
 #define ENTT_REGISTRY_HPP
 
 
-#include <tuple>
 #include <vector>
 #include <bitset>
 #include <utility>
 #include <cstddef>
 #include <iterator>
 #include <cassert>
+#include <type_traits>
 #include "component_pool.hpp"
 #include "ident.hpp"
 
@@ -20,20 +20,22 @@ template<typename...>
 class View;
 
 
-template<template<typename...> class Pool, typename Entity, typename... Components, typename Type, typename... Types, typename... Filters>
-class View<Pool<Entity, Components...>, std::tuple<Type, Types...>, std::tuple<Filters...>> final {
+template<template<typename...> class Pool, typename Entity, typename... Components, typename Type, typename... Types>
+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>;
 
+    class ViewIterator;
+
+public:
+    using iterator_type = ViewIterator;
+    using size_type = typename pool_type::size_type;
+
+private:
     class ViewIterator {
-        bool valid() const noexcept {
-            using accumulator_type = bool[];
-            auto &bitmask = mask[entities[pos-1]];
-            bool all = bitmask.test(ident<Components...>.template get<Type>());
-            accumulator_type types = { true, (all = all && bitmask.test(ident<Components...>.template get<Types>()))... };
-            accumulator_type filters = { true, (all = all && !bitmask.test(ident<Components...>.template get<Filters>()))... };
-            return void(types), void(filters), all;
+        inline bool valid() const noexcept {
+            return ((mask[entities[pos]] & bitmask) == bitmask);
         }
 
     public:
@@ -43,14 +45,15 @@ class View<Pool<Entity, Components...>, std::tuple<Type, Types...>, std::tuple<F
         using pointer = entity_type *;
         using iterator_category = std::input_iterator_tag;
 
-        ViewIterator(pool_type &pool, const entity_type *entities, typename pool_type::size_type pos, mask_type *mask) noexcept
-            : pool{pool}, entities{entities}, pos{pos}, mask{mask}
+        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}
         {
-            if(this->pos) { while(!valid() && --this->pos); }
+            while(this->pos != last && !valid()) { ++this->pos; }
         }
 
         ViewIterator & operator++() noexcept {
-            if(pos) { while(--pos && !valid()); }
+            ++pos;
+            while(pos != last && !valid()) { ++pos; }
             return *this;
         }
 
@@ -60,7 +63,7 @@ class View<Pool<Entity, Components...>, std::tuple<Type, Types...>, std::tuple<F
         }
 
         bool operator==(const ViewIterator &other) const noexcept {
-            return other.entities == entities && other.pos == pos && other.mask == mask;
+            return other.pos == pos && other.entities == entities;
         }
 
         bool operator!=(const ViewIterator &other) const noexcept {
@@ -68,14 +71,15 @@ class View<Pool<Entity, Components...>, std::tuple<Type, Types...>, std::tuple<F
         }
 
         value_type operator*() const noexcept {
-            return *(entities+pos-1);
+            return entities[pos];
         }
 
     private:
-        pool_type &pool;
+        const mask_type bitmask;
         const entity_type *entities;
-        typename pool_type::size_type pos;
-        mask_type *mask;
+        const mask_type *mask;
+        size_type pos;
+        size_type last;
     };
 
     template<typename Comp>
@@ -89,34 +93,25 @@ class View<Pool<Entity, Components...>, std::tuple<Type, Types...>, std::tuple<F
     }
 
 public:
-    using iterator_type = ViewIterator;
-    using size_type = typename pool_type::size_type;
-
-    template<typename... Comp>
-    using view_type = View<pool_type, std::tuple<Type, Types...>, std::tuple<Comp...>>;
-
-    explicit View(pool_type &pool, mask_type *mask) noexcept
+    explicit View(const pool_type &pool, const mask_type *mask) noexcept
         : entities{pool.template entities<Type>()},
-          size{pool.template size<Type>()},
           pool{pool},
-          mask{mask}
+          mask{mask},
+          size{pool.template size<Type>()}
     {
         using accumulator_type = int[];
-        accumulator_type accumulator = { 0, (prefer<Types>(), 0)... };
-        (void)accumulator;
-    }
-
-    template<typename... Comp>
-    view_type<Comp...> exclude() noexcept {
-        return view_type<Comp...>{pool, mask};
+        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)... };
+        (void)types, (void)pref;
     }
 
     iterator_type begin() const noexcept {
-        return ViewIterator{pool, entities, size, mask};
+        return ViewIterator{bitmask, entities, mask, 0, size};
     }
 
     iterator_type end() const noexcept {
-        return ViewIterator{pool, entities, 0, mask};
+        return ViewIterator{bitmask, entities, mask, size, size};
     }
 
     void reset() noexcept {
@@ -129,18 +124,25 @@ public:
 
 private:
     const entity_type *entities;
+    const pool_type &pool;
+    const mask_type *mask;
     size_type size;
-    pool_type &pool;
-    mask_type *mask;
+    mask_type bitmask;
 };
 
 
-template<template<typename...> class Pool, typename Entity, typename... Components, typename Type>
-class View<Pool<Entity, Components...>, std::tuple<Type>, std::tuple<>> final {
+    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;
-    using mask_type = std::bitset<sizeof...(Components)+1>;
 
+    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;
@@ -148,12 +150,12 @@ class View<Pool<Entity, Components...>, std::tuple<Type>, std::tuple<>> final {
         using pointer = entity_type *;
         using iterator_category = std::input_iterator_tag;
 
-        ViewIterator(const entity_type *entities, typename pool_type::size_type pos) noexcept
+        ViewIterator(const entity_type *entities, size_type pos) noexcept
             : entities{entities}, pos{pos}
         {}
 
         ViewIterator & operator++() noexcept {
-            --pos;
+            ++pos;
             return *this;
         }
 
@@ -163,7 +165,7 @@ class View<Pool<Entity, Components...>, std::tuple<Type>, std::tuple<>> final {
         }
 
         bool operator==(const ViewIterator &other) const noexcept {
-            return other.entities == entities && other.pos == pos;
+            return other.pos == pos && other.entities == entities;
         }
 
         bool operator!=(const ViewIterator &other) const noexcept {
@@ -171,36 +173,25 @@ class View<Pool<Entity, Components...>, std::tuple<Type>, std::tuple<>> final {
         }
 
         value_type operator*() const noexcept {
-            return *(entities+pos-1);
+            return entities[pos];
         }
 
     private:
         const entity_type *entities;
-        typename pool_type::size_type pos;
+        size_type pos;
     };
 
 public:
-    using iterator_type = ViewIterator;
-    using size_type = typename pool_type::size_type;
-
-    template<typename... Comp>
-    using view_type = View<pool_type, std::tuple<Type>, std::tuple<Comp...>>;
-
-    explicit View(pool_type &pool, mask_type *mask) noexcept
-        : pool{pool}, mask{mask}
+    explicit View(const pool_type &pool) noexcept
+        : pool{pool}
     {}
 
-    template<typename... Comp>
-    view_type<Comp...> exclude() noexcept {
-        return view_type<Comp...>{pool, mask};
-    }
-
     iterator_type begin() const noexcept {
-        return ViewIterator{pool.template entities<Type>(), pool.template size<Type>()};
+        return ViewIterator{pool.template entities<Type>(), 0};
     }
 
     iterator_type end() const noexcept {
-        return ViewIterator{pool.template entities<Type>(), 0};
+        return ViewIterator{pool.template entities<Type>(), pool.template size<Type>()};
     }
 
     size_type size() const noexcept {
@@ -208,8 +199,7 @@ public:
     }
 
 private:
-    pool_type &pool;
-    mask_type *mask;
+    const pool_type &pool;
 };
 
 
@@ -254,7 +244,7 @@ private:
 
 public:
     template<typename... Comp>
-    using view_type = View<pool_type, std::tuple<Comp...>, std::tuple<>>;
+    using view_type = View<pool_type, Comp...>;
 
     template<typename... Args>
     Registry(Args&&... args)
@@ -416,9 +406,12 @@ public:
     }
 
     template<typename... Comp>
-    view_type<Comp...> view() noexcept {
-        return view_type<Comp...>{pool, entities.data()};
-    }
+    std::enable_if_t<(sizeof...(Comp) == 1), view_type<Comp...>>
+    view() noexcept { return view_type<Comp...>{pool}; }
+
+    template<typename... Comp>
+    std::enable_if_t<(sizeof...(Comp) > 1), view_type<Comp...>>
+    view() noexcept { return view_type<Comp...>{pool, entities.data()}; }
 
 private:
     std::vector<mask_type> entities;

+ 140 - 0
test/benchmark.cpp

@@ -150,6 +150,60 @@ TEST(DefaultRegistry, IterateTwoComponents10M) {
     registry.reset();
 }
 
+TEST(DefaultRegistry, IterateTwoComponents10MHalf) {
+    using registry_type = entt::DefaultRegistry<Position, Velocity>;
+
+    registry_type registry;
+
+    std::cout << "Iterating over 10000000 entities, two components, half of the entities have all the components" << std::endl;
+
+    for (uint64_t i = 0; i < 10000000L; i++) {
+        auto entity = registry.create<Velocity>();
+        if(i % 2) { registry.assign<Position>(entity); }
+    }
+
+    Timer timer;
+
+    auto view = registry.view<Position, Velocity>();
+
+    for(auto entity: view) {
+        auto &position = registry.get<Position>(entity);
+        auto &velocity = registry.get<Velocity>(entity);
+        (void)position;
+        (void)velocity;
+    }
+
+    timer.elapsed();
+    registry.reset();
+}
+
+TEST(DefaultRegistry, IterateTwoComponents10MOne) {
+    using registry_type = entt::DefaultRegistry<Position, Velocity>;
+
+    registry_type registry;
+
+    std::cout << "Iterating over 10000000 entities, two components, only one entity has all the components" << std::endl;
+
+    for (uint64_t i = 0; i < 10000000L; i++) {
+        auto entity = registry.create<Velocity>();
+        if(i == 5000000L) { registry.assign<Position>(entity); }
+    }
+
+    Timer timer;
+
+    auto view = registry.view<Position, Velocity>();
+
+    for(auto entity: view) {
+        auto &position = registry.get<Position>(entity);
+        auto &velocity = registry.get<Velocity>(entity);
+        (void)position;
+        (void)velocity;
+    }
+
+    timer.elapsed();
+    registry.reset();
+}
+
 TEST(DefaultRegistry, IterateSingleComponent50M) {
     using registry_type = entt::DefaultRegistry<Position, Velocity>;
 
@@ -273,3 +327,89 @@ TEST(DefaultRegistry, IterateTenComponents10M) {
     timer.elapsed();
     registry.reset();
 }
+
+TEST(DefaultRegistry, IterateTenComponents10MHalf) {
+    using registry_type = entt::DefaultRegistry<Position, Velocity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8>;
+
+    registry_type registry;
+
+    std::cout << "Iterating over 10000000 entities, ten components, half of the entities have all the components" << std::endl;
+
+    for (uint64_t i = 0; i < 10000000L; i++) {
+        auto entity = registry.create<Velocity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8>();
+        if(i % 2) { registry.assign<Position>(entity); }
+    }
+
+    Timer timer;
+
+    auto view = registry.view<Position, Velocity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8>();
+
+    for(auto entity: view) {
+        auto &position = registry.get<Position>(entity);
+        auto &velocity = registry.get<Velocity>(entity);
+        auto &comp1 = registry.get<Comp1>(entity);
+        auto &comp2 = registry.get<Comp2>(entity);
+        auto &comp3 = registry.get<Comp3>(entity);
+        auto &comp4 = registry.get<Comp4>(entity);
+        auto &comp5 = registry.get<Comp5>(entity);
+        auto &comp6 = registry.get<Comp6>(entity);
+        auto &comp7 = registry.get<Comp7>(entity);
+        auto &comp8 = registry.get<Comp8>(entity);
+        (void)position;
+        (void)velocity;
+        (void)comp1;
+        (void)comp2;
+        (void)comp3;
+        (void)comp4;
+        (void)comp5;
+        (void)comp6;
+        (void)comp7;
+        (void)comp8;
+    }
+
+    timer.elapsed();
+    registry.reset();
+}
+
+TEST(DefaultRegistry, IterateTenComponents10MOne) {
+    using registry_type = entt::DefaultRegistry<Position, Velocity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8>;
+
+    registry_type registry;
+
+    std::cout << "Iterating over 10000000 entities, ten components, only one entity has all the components" << std::endl;
+
+    for (uint64_t i = 0; i < 10000000L; i++) {
+        auto entity = registry.create<Velocity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8>();
+        if(i == 5000000L) { registry.assign<Position>(entity); }
+    }
+
+    Timer timer;
+
+    auto view = registry.view<Position, Velocity, Comp1, Comp2, Comp3, Comp4, Comp5, Comp6, Comp7, Comp8>();
+
+    for(auto entity: view) {
+        auto &position = registry.get<Position>(entity);
+        auto &velocity = registry.get<Velocity>(entity);
+        auto &comp1 = registry.get<Comp1>(entity);
+        auto &comp2 = registry.get<Comp2>(entity);
+        auto &comp3 = registry.get<Comp3>(entity);
+        auto &comp4 = registry.get<Comp4>(entity);
+        auto &comp5 = registry.get<Comp5>(entity);
+        auto &comp6 = registry.get<Comp6>(entity);
+        auto &comp7 = registry.get<Comp7>(entity);
+        auto &comp8 = registry.get<Comp8>(entity);
+        (void)position;
+        (void)velocity;
+        (void)comp1;
+        (void)comp2;
+        (void)comp3;
+        (void)comp4;
+        (void)comp5;
+        (void)comp6;
+        (void)comp7;
+        (void)comp8;
+    }
+
+    timer.elapsed();
+    registry.reset();
+}

+ 6 - 48
test/registry.cpp

@@ -157,6 +157,9 @@ TEST(DefaultRegistry, ViewSingleComponent) {
     registry_type::entity_type e1 = registry.create();
     registry_type::entity_type e2 = registry.create<int, char>();
 
+    ASSERT_NO_THROW(registry.view<char>().begin()++);
+    ASSERT_NO_THROW(++registry.view<char>().begin());
+
     auto view = registry.view<char>();
 
     ASSERT_NE(view.begin(), view.end());
@@ -171,9 +174,6 @@ TEST(DefaultRegistry, ViewSingleComponent) {
 
     ASSERT_EQ(view.begin(), view.end());
     ASSERT_NO_THROW(registry.reset());
-
-    ASSERT_NO_THROW(registry.view<char>().begin()++);
-    ASSERT_NO_THROW(++registry.view<char>().begin());
 }
 
 TEST(DefaultRegistry, ViewMultipleComponent) {
@@ -184,6 +184,9 @@ TEST(DefaultRegistry, ViewMultipleComponent) {
     registry_type::entity_type e1 = registry.create<char>();
     registry_type::entity_type e2 = registry.create<int, char>();
 
+    ASSERT_NO_THROW((registry.view<int, char>().begin()++));
+    ASSERT_NO_THROW((++registry.view<int, char>().begin()));
+
     auto view = registry.view<int, char>();
 
     ASSERT_NE(view.begin(), view.end());
@@ -194,9 +197,6 @@ TEST(DefaultRegistry, ViewMultipleComponent) {
 
     ASSERT_EQ(view.begin(), view.end());
     ASSERT_NO_THROW(registry.reset());
-
-    ASSERT_NO_THROW((registry.view<int, char>().begin()++));
-    ASSERT_NO_THROW((++registry.view<int, char>().begin()));
 }
 
 TEST(DefaultRegistry, EmptyViewSingleComponent) {
@@ -231,45 +231,3 @@ TEST(DefaultRegistry, EmptyViewMultipleComponent) {
 
     registry.reset();
 }
-
-TEST(DefaultRegistry, ViewSingleComponentWithExclude) {
-    using registry_type = entt::DefaultRegistry<int, char>;
-
-    registry_type registry;
-
-    registry_type::entity_type e1 = registry.create<char>();
-    registry_type::entity_type e2 = registry.create<int, char>();
-
-    auto view = registry.view<char>().exclude<int>();
-
-    ASSERT_NE(view.begin(), view.end());
-
-    ASSERT_EQ(*view.begin(), e1);
-    ASSERT_NE(*view.begin(), e2);
-    ASSERT_EQ(++view.begin(), view.end());
-    ASSERT_NO_THROW(registry.reset());
-
-    ASSERT_NO_THROW((registry.view<char>().exclude<int>().begin()++));
-    ASSERT_NO_THROW((++registry.view<char>().exclude<int>().begin()));
-}
-
-TEST(DefaultRegistry, ViewMultipleComponentWithExclude) {
-    using registry_type = entt::DefaultRegistry<int, char, double>;
-
-    registry_type registry;
-
-    registry_type::entity_type e1 = registry.create<int, char, double>();
-    registry_type::entity_type e2 = registry.create<char, double>();
-
-    auto view = registry.view<char, double>().exclude<int>();
-
-    ASSERT_NE(view.begin(), view.end());
-
-    ASSERT_NE(*view.begin(), e1);
-    ASSERT_EQ(*view.begin(), e2);
-    ASSERT_EQ(++view.begin(), view.end());
-    ASSERT_NO_THROW(registry.reset());
-
-    ASSERT_NO_THROW((registry.view<char>().exclude<int>().begin()++));
-    ASSERT_NO_THROW((++registry.view<char>().exclude<int>().begin()));
-}