Michele Caini 8 лет назад
Родитель
Сommit
75cb2cd1f7

+ 2 - 2
README.md

@@ -168,8 +168,8 @@ Dell XPS 13 out of the mid 2014):
 | Standard view, 10M entities, ten components<br/>Half of the entities have all the components | **0.0090s** | 0.0620s |
 | Standard view, 10M entities, ten components<br/>One of the entities has all the components | 0.0070s | **1.3e-06s** |
 | Persistent view, 10M entities, ten components | 0.0105s | **6.2e-07s** |
-| Sort 150k entities, one component | - | **0.0084s** |
-| Sort 150k entities, enforce permutation | - | **0.0067s** |
+| Sort 150k entities, one component<br/>Arrays are in reverse order | - | **0.0043s** |
+| Sort 150k entities, enforce permutation<br/>Arrays are in reverse order | - | **0.0006s** |
 
 `EnTT` includes its own tests and benchmarks. See
 [benchmark.cpp](https://github.com/skypjack/entt/blob/master/test/benchmark.cpp)

+ 1 - 5
src/entt/entity/registry.hpp

@@ -694,11 +694,7 @@ public:
      */
     template<typename Component, typename Compare>
     void sort(Compare compare) {
-        auto &cpool = ensure<Component>();
-
-        cpool.sort([&cpool, compare = std::move(compare)](auto lhs, auto rhs) {
-            return compare(static_cast<const Component &>(cpool.get(lhs)), static_cast<const Component &>(cpool.get(rhs)));
-        });
+        ensure<Component>().sort(std::move(compare));
     }
 
     /**

+ 97 - 74
src/entt/entity/sparse_set.hpp

@@ -3,6 +3,7 @@
 
 
 #include <algorithm>
+#include <numeric>
 #include <utility>
 #include <vector>
 #include <cstddef>
@@ -281,50 +282,18 @@ public:
      * An assertion will abort the execution at runtime in debug mode if the
      * sparse set doesn't contain the given entities.
      *
-     * @param lhs A valid entity identifier.
-     * @param rhs A valid entity identifier.
+     * @param lhs A valid position within the sparse set.
+     * @param rhs A valid position within the sparse set.
      */
-    virtual void swap(entity_type lhs, entity_type rhs) {
-        assert(has(lhs));
-        assert(has(rhs));
-        auto &le = reverse[lhs & traits_type::entity_mask];
-        auto &re = reverse[rhs & traits_type::entity_mask];
-        // we must get rid of the in-use bit for it's not part of the position
-        std::swap(direct[le & ~in_use], direct[re & ~in_use]);
-        std::swap(le, re);
-    }
-
-    /**
-     * @brief Sort entities according to the given comparison function.
-     *
-     * Sort the elements so that iterating the sparse set with a couple of
-     * iterators returns them in the expected order. See `begin` and `end` for
-     * more details.
-     *
-     * @note
-     * Attempting to iterate elements using the raw pointer returned by `data`
-     * gives no guarantees on the order, even though `sort` has been invoked.
-     *
-     * @tparam Compare Type of the comparison function.
-     * @param compare A comparison function whose signature shall be equivalent
-     * to: `bool(Entity, Entity)`.
-     */
-    template<typename Compare>
-    void sort(Compare compare) {
-        std::vector<pos_type> copy{direct.cbegin(), direct.cend()};
-        std::sort(copy.begin(), copy.end(), [compare = std::move(compare)](auto... args) {
-            return !compare(args...);
-        });
-
-        for(pos_type i = 0; i < copy.size(); ++i) {
-            if(direct[i] != copy[i]) {
-                swap(direct[i], copy[i]);
-            }
-        }
+    void swap(pos_type lhs, pos_type rhs) noexcept {
+        assert(lhs < direct.size());
+        assert(rhs < direct.size());
+        std::swap(reverse[direct[lhs]], reverse[direct[rhs]]);
+        std::swap(direct[lhs], direct[rhs]);
     }
 
     /**
-     * @brief Sort entities according to their order in a sparse set.
+     * @brief Sort entities according to their order in another sparse set.
      *
      * Entities that are part of both the sparse sets are ordered internally
      * according to the order they have in `other`. All the other entities goes
@@ -342,32 +311,23 @@ public:
      *
      * @param other The sparse sets that imposes the order of the entities.
      */
-    void respect(const SparseSet<Entity> &other) {
-        struct Bool { bool value{false}; };
-        std::vector<Bool> check(std::max(other.reverse.size(), reverse.size()));
-
-        for(auto entity: other.direct) {
-            check[entity & traits_type::entity_mask].value = true;
-        }
+    virtual void respect(const SparseSet<Entity> &other) noexcept {
+        auto from = other.begin();
+        auto to = other.end();
 
-        sort([this, &other, &check](auto lhs, auto rhs) {
-            const auto le = lhs & traits_type::entity_mask;
-            const auto re = rhs & traits_type::entity_mask;
+        pos_type pos = direct.size() - 1;
 
-            const bool bLhs = check[le].value;
-            const bool bRhs = check[re].value;
-            bool compare = false;
+        while(pos > 0 && from != to) {
+            if(has(*from)) {
+                if(*from != direct[pos]) {
+                    swap(pos, get(*from));
+                }
 
-            if(bLhs && bRhs) {
-                compare = other.get(rhs) < other.get(lhs);
-            } else if(!bLhs && !bRhs) {
-                compare = re < le;
-            } else {
-                compare = bLhs;
+                --pos;
             }
 
-            return compare;
-        });
+            ++from;
+        }
     }
 
     /**
@@ -546,24 +506,87 @@ public:
     }
 
     /**
-     * @brief Swaps two entities and their objects.
+     * @brief Sort components according to the given comparison function.
+     *
+     * Sort the elements so that iterating the sparse set with a couple of
+     * iterators returns them in the expected order. See `begin` and `end` for
+     * more details.
      *
      * @note
-     * This function doesn't swap objects between entities. It exchanges entity
-     * and object positions in the sparse set. It's used mainly for sorting.
+     * Attempting to iterate elements using the raw pointer returned by `data`
+     * gives no guarantees on the order, even though `sort` has been invoked.
      *
-     * @warning
-     * Attempting to use entities that don't belong to the sparse set results
-     * in undefined behavior.<br/>
-     * An assertion will abort the execution at runtime in debug mode if the
-     * sparse set doesn't contain the given entities.
+     * @tparam Compare Type of the comparison function.
+     * @param compare A comparison function whose signature shall be equivalent
+     * to: `bool(const Type &, const Type &)`.
+     */
+    template<typename Compare>
+    void sort(Compare compare) {
+        std::vector<pos_type> copy(instances.size());
+        std::iota(copy.begin(), copy.end(), 0);
+
+        std::sort(copy.begin(), copy.end(), [this, compare = std::move(compare)](auto lhs, auto rhs) {
+            return compare(const_cast<const object_type &>(instances[rhs]), const_cast<const object_type &>(instances[lhs]));
+        });
+
+        for(pos_type i = 0; i < copy.size(); ++i) {
+            auto curr = i;
+            auto next = copy[curr];
+
+            while(curr != next) {
+                auto lhs = copy[curr];
+                auto rhs = copy[next];
+                std::swap(instances[lhs], instances[rhs]);
+                underlying_type::swap(lhs, rhs);
+                copy[curr] = curr;
+                curr = next;
+                next = copy[curr];
+            }
+        }
+    }
+
+    /**
+     * @brief Sort components according to the order of the entities in another
+     * sparse set.
      *
-     * @param lhs A valid entity identifier.
-     * @param rhs A valid entity identifier.
+     * Entities that are part of both the sparse sets are ordered internally
+     * according to the order they have in `other`. All the other entities goes
+     * to the end of the list and there are no guarantess on their order.
+     * Components are sorted according to the entities to which they
+     * belong.<br/>
+     * In other terms, this function can be used to impose the same order on two
+     * sets by using one of them as a master and the other one as a slave.
+     *
+     * Iterating the sparse set with a couple of iterators returns elements in
+     * the expected order after a call to `sort`. See `begin` and `end` for more
+     * details.
+     *
+     * @note
+     * Attempting to iterate elements using the raw pointer returned by `data`
+     * gives no guarantees on the order, even though `sort` has been invoked.
+     *
+     * @param other The sparse sets that imposes the order of the entities.
      */
-    void swap(entity_type lhs, entity_type rhs) override {
-        std::swap(instances[underlying_type::get(lhs)], instances[underlying_type::get(rhs)]);
-        underlying_type::swap(lhs, rhs);
+    void respect(const SparseSet<Entity> &other) noexcept override {
+        auto from = other.begin();
+        auto to = other.end();
+
+        pos_type pos = underlying_type::size() - 1;
+        const auto *direct = underlying_type::data();
+
+        while(pos > 0 && from != to) {
+            if(underlying_type::has(*from)) {
+                if(*from != *(direct + pos)) {
+                    auto candidate = underlying_type::get(*from);
+                    std::swap(instances[pos], instances[candidate]);
+                    underlying_type::swap(pos, candidate);
+                }
+
+                --pos;
+            }
+
+            ++from;
+        }
     }
 
     /**

+ 1 - 1
src/entt/signal/dispatcher.hpp

@@ -40,7 +40,7 @@ class Dispatcher final {
 
     template<typename Event>
     struct SignalWrapper final: BaseSignalWrapper {
-        void publish(std::size_t current) final override {
+        void publish(std::size_t current) override {
             for(auto &&event: events[current]) {
                 signal.publish(event);
             }

+ 3 - 3
test/entt/entity/sparse_set.cpp

@@ -160,7 +160,7 @@ TEST(SparseSetWithType, SortOrdered) {
     ASSERT_EQ(set.get(9), 1);
 
     set.sort([&set](auto lhs, auto rhs) {
-        return set.get(lhs) < set.get(rhs);
+        return lhs < rhs;
     });
 
     ASSERT_EQ(*(set.raw() + 0u), 12);
@@ -196,7 +196,7 @@ TEST(SparseSetWithType, SortReverse) {
     ASSERT_EQ(set.get(9), 12);
 
     set.sort([&set](auto lhs, auto rhs) {
-        return set.get(lhs) < set.get(rhs);
+        return lhs < rhs;
     });
 
     ASSERT_EQ(*(set.raw() + 0u), 12);
@@ -232,7 +232,7 @@ TEST(SparseSetWithType, SortUnordered) {
     ASSERT_EQ(set.get(9), 12);
 
     set.sort([&set](auto lhs, auto rhs) {
-        return set.get(lhs) < set.get(rhs);
+        return lhs < rhs;
     });
 
     ASSERT_EQ(*(set.raw() + 0u), 12);