diff --git a/README.md b/README.md
index cd8a62659..6fa9f65f7 100644
--- a/README.md
+++ b/README.md
@@ -168,8 +168,8 @@ Dell XPS 13 out of the mid 2014):
| Standard view, 10M entities, ten components
Half of the entities have all the components | **0.0090s** | 0.0620s |
| Standard view, 10M entities, ten components
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
Arrays are in reverse order | - | **0.0043s** |
+| Sort 150k entities, enforce permutation
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)
diff --git a/src/entt/entity/registry.hpp b/src/entt/entity/registry.hpp
index 5d549ef71..c6840c08f 100644
--- a/src/entt/entity/registry.hpp
+++ b/src/entt/entity/registry.hpp
@@ -694,11 +694,7 @@ public:
*/
template
void sort(Compare compare) {
- auto &cpool = ensure();
-
- cpool.sort([&cpool, compare = std::move(compare)](auto lhs, auto rhs) {
- return compare(static_cast(cpool.get(lhs)), static_cast(cpool.get(rhs)));
- });
+ ensure().sort(std::move(compare));
}
/**
diff --git a/src/entt/entity/sparse_set.hpp b/src/entt/entity/sparse_set.hpp
index 7cd1f19ce..85422d60c 100644
--- a/src/entt/entity/sparse_set.hpp
+++ b/src/entt/entity/sparse_set.hpp
@@ -3,6 +3,7 @@
#include
+#include
#include
#include
#include
@@ -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);
+ 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 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
- void sort(Compare compare) {
- std::vector 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]);
- }
- }
- }
-
- /**
- * @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 &other) {
- struct Bool { bool value{false}; };
- std::vector check(std::max(other.reverse.size(), reverse.size()));
+ virtual void respect(const SparseSet &other) noexcept {
+ auto from = other.begin();
+ auto to = other.end();
- for(auto entity: other.direct) {
- check[entity & traits_type::entity_mask].value = true;
- }
+ pos_type pos = direct.size() - 1;
- sort([this, &other, &check](auto lhs, auto rhs) {
- const auto le = lhs & traits_type::entity_mask;
- const auto re = rhs & traits_type::entity_mask;
+ while(pos > 0 && from != to) {
+ if(has(*from)) {
+ if(*from != direct[pos]) {
+ swap(pos, get(*from));
+ }
- const bool bLhs = check[le].value;
- const bool bRhs = check[re].value;
- bool compare = false;
-
- 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.
- * 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.
+ * @tparam Compare Type of the comparison function.
+ * @param compare A comparison function whose signature shall be equivalent
+ * to: `bool(const Type &, const Type &)`.
*/
- 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);
+ template
+ void sort(Compare compare) {
+ std::vector 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(instances[rhs]), const_cast(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.
+ *
+ * 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.
+ * 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 respect(const SparseSet &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;
+ }
}
/**
diff --git a/src/entt/signal/dispatcher.hpp b/src/entt/signal/dispatcher.hpp
index 5eb0fc65b..b0aca97c2 100644
--- a/src/entt/signal/dispatcher.hpp
+++ b/src/entt/signal/dispatcher.hpp
@@ -40,7 +40,7 @@ class Dispatcher final {
template
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);
}
diff --git a/test/entt/entity/sparse_set.cpp b/test/entt/entity/sparse_set.cpp
index d68b1a588..a1c8d18c7 100644
--- a/test/entt/entity/sparse_set.cpp
+++ b/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);