sorting no longer requires allocations and is much faster
This commit is contained in:
61
README.md
61
README.md
@@ -183,47 +183,36 @@ amazing set of features. And even more, of course.
|
||||
## Performance
|
||||
|
||||
As it stands right now, `EnTT` is just fast enough for my requirements when
|
||||
compared to my first choice (it was already amazingly fast actually).<br/>
|
||||
Below is a comparison between the two (both of them compiled with GCC 7.3.0 on a
|
||||
Dell XPS 13 from mid 2014):
|
||||
compared to my first choice (it was already amazingly fast actually).
|
||||
|
||||
| Benchmark | EntityX (compile-time) | EnTT |
|
||||
|-----------|-------------|-------------|
|
||||
| Create 1M entities | 0.0147s | **0.0046s** |
|
||||
| Destroy 1M entities | 0.0053s | **0.0045s** |
|
||||
| 1M entities, one component | 0.0012s | **1.9e-07s** |
|
||||
| 1M entities, two components | 0.0012s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>Half of the entities have all the components | 0.0009s | **3.8e-07s** |
|
||||
| 1M entities, two components<br/>One of the entities has all the components | 0.0008s | **1.0e-06s** |
|
||||
| 1M entities, five components | 0.0010s | **7.0e-07s** |
|
||||
| 1M entities, ten components | 0.0011s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>Half of the entities have all the components | 0.0010s | **1.2e-06s** |
|
||||
| 1M entities, ten components<br/>One of the entities has all the components | 0.0008s | **1.2e-06s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are in reverse order | - | **0.0036s** |
|
||||
| Sort 150k entities, enforce permutation<br/>Arrays are in reverse order | - | **0.0005s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are almost sorted, std::sort | - | **0.0035s** |
|
||||
| Sort 150k entities, one component<br/>Arrays are almost sorted, insertion sort | - | **0.0007s** |
|
||||
For a long time, this file contained also some benchmarks to show how fast
|
||||
`EnTT` was. However, I got tired of updating them whenever there is an
|
||||
improvement. Furthermore, there are a lot of projects out there that use `EnTT`
|
||||
as a basis for comparison (this should already tell you a lot) and offer their
|
||||
own more or less ad hoc results to show how they perform well downhill and with
|
||||
the wind at their back.<br/>
|
||||
Many of these benchmarks are completely wrong and cannot be used to evaluate any
|
||||
of the existing libraries, many others are simply incomplete, good at omitting
|
||||
some information and using the wrong function to compare a given feature.
|
||||
Certainly there are also good ones but they age quickly if nobody updates them,
|
||||
especially when the library they are dealing with is actively developed.<br/>
|
||||
Do you really want to have useless numbers on yet another README file?
|
||||
|
||||
Note: The default version of `EntityX` (`master` branch) wasn't added to the
|
||||
comparison because it's already much slower than its compile-time counterpart.
|
||||
If you are interested, you can compile the `benchmark` test in release mode (to
|
||||
enable compiler optimizations, otherwise it would make little sense) by setting
|
||||
the `BUILD_BENCHMARK` option to `ON`, then evaluate yourself whether you're
|
||||
satisfied with the results or not.
|
||||
|
||||
Pretty interesting results, aren't them? In fact, these benchmarks are the ones
|
||||
used by `EntityX` to show _how fast it is_. To be honest, they aren't so good
|
||||
and these results shouldn't be taken too seriously (indeed they are completely
|
||||
unrealistic).<br/>
|
||||
The proposed entity-component system is incredibly fast to iterate entities,
|
||||
this is a fact. The compiler can make a lot of optimizations because of how
|
||||
`EnTT` works, even more when components aren't used at all. This is exactly the
|
||||
case for these benchmarks. On the other hand, if we consider real world cases,
|
||||
`EnTT` is somewhere between a bit and much faster than the other solutions
|
||||
around when users also access the components and not just the entities, although
|
||||
it isn't as fast as reported by these benchmarks.<br/>
|
||||
This is why they are completely wrong and cannot be used to evaluate any of the
|
||||
entity-component-system libraries out there.
|
||||
The proposed entity-component system is incredibly fast to iterate entities and
|
||||
components, this is a fact. Some compilers make a lot of optimizations because
|
||||
of how `EnTT` works, even more when components aren't used at all. In general,
|
||||
if we consider real world cases, `EnTT` is somewhere between a bit and much
|
||||
faster than many of the other solutions around, although I couldn't check them
|
||||
all for obvious reasons.
|
||||
|
||||
The choice to use `EnTT` should be based on its carefully designed API, its
|
||||
set of features and the general performance, not because some single benchmark
|
||||
shows it to be the fastest tool available.
|
||||
set of features and the general performance, **not** because some single
|
||||
benchmark shows it to be the fastest tool available.
|
||||
|
||||
In the future I'll likely try to get even better performance while still adding
|
||||
new features, mainly for fun.<br/>
|
||||
|
||||
1
TODO
1
TODO
@@ -33,4 +33,3 @@ TODO
|
||||
* nested groups: AB/ABC/ABCD/... (hints: sort, check functions)
|
||||
* multi component registry::remove and some others?
|
||||
* range based registry::remove and some others?
|
||||
* allocation-less and faster sort
|
||||
|
||||
@@ -378,10 +378,6 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* The comparison function object received by the sort function object
|
||||
* hasn't necessarily the type of the one passed along with the other
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* either `data` or `raw` gives no guarantees on the order, even though
|
||||
@@ -400,10 +396,13 @@ public:
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>);
|
||||
handler->sort(handler->begin(), handler->end(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else if constexpr(sizeof...(Component) == 1) {
|
||||
handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
return compare((std::get<pool_type<Component> *>(pools)->get(lhs), ...), (std::get<pool_type<Component> *>(pools)->get(rhs), ...));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
// useless this-> used to suppress a warning with clang
|
||||
return compare(this->get<Component...>(lhs), this->get<Component...>(rhs));
|
||||
return compare(std::tuple<decltype(get<Component>({}))...>{std::get<pool_type<Component> *>(pools)->get(lhs)...}, std::tuple<decltype(get<Component>({}))...>{std::get<pool_type<Component> *>(pools)->get(rhs)...});
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
@@ -801,10 +800,6 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* The comparison function object received by the sort function object
|
||||
* hasn't necessarily the type of the one passed along with the other
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* either `data` or `raw` gives no guarantees on the order, even though
|
||||
@@ -825,10 +820,13 @@ public:
|
||||
if constexpr(sizeof...(Component) == 0) {
|
||||
static_assert(std::is_invocable_v<Compare, const entity_type, const entity_type>);
|
||||
cpool->sort(cpool->end()-*length, cpool->end(), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
} else if constexpr(sizeof...(Component) == 1) {
|
||||
cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
return compare((std::get<pool_type<Component> *>(pools)->get(lhs), ...), (std::get<pool_type<Component> *>(pools)->get(rhs), ...));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) {
|
||||
// useless this-> used to suppress a warning with clang
|
||||
return compare(this->get<Component...>(lhs), this->get<Component...>(rhs));
|
||||
return compare(std::tuple<decltype(get<Component>({}))...>{std::get<pool_type<Component> *>(pools)->get(lhs)...}, std::tuple<decltype(get<Component>({}))...>{std::get<pool_type<Component> *>(pools)->get(rhs)...});
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
|
||||
@@ -499,10 +499,6 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* The comparison function object received by the sort function object
|
||||
* hasn't necessarily the type of the one passed along with the other
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* `data` gives no guarantees on the order, even though `sort` has been
|
||||
@@ -519,25 +515,74 @@ public:
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(first > last));
|
||||
ENTT_ASSERT(!(last < first));
|
||||
ENTT_ASSERT(!(last > end()));
|
||||
|
||||
std::vector<size_type> copy(last - first);
|
||||
const auto offset = std::distance(last, end());
|
||||
std::iota(copy.begin(), copy.end(), size_type{});
|
||||
const auto length = std::distance(first, last);
|
||||
const auto skip = std::distance(last, end());
|
||||
const auto to = direct.rend() - skip;
|
||||
const auto from = to - length;
|
||||
|
||||
algo(copy.rbegin(), copy.rend(), [this, offset, compare = std::move(compare)](const auto lhs, const auto rhs) {
|
||||
return compare(std::as_const(direct[lhs+offset]), std::as_const(direct[rhs+offset]));
|
||||
}, std::forward<Args>(args)...);
|
||||
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
||||
|
||||
for(size_type pos{}, length = copy.size(); pos < length; ++pos) {
|
||||
for(size_type pos = skip, end = skip+length; pos < end; pos++) {
|
||||
auto [page, offset] = map(direct[pos]);
|
||||
reverse[page][offset] = entity_type(pos);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sort elements according to the given comparison function.
|
||||
*
|
||||
* @sa sort
|
||||
*
|
||||
* This function is a slightly slower version of `sort` that invokes the
|
||||
* caller to indicate which entities are swapped.<br/>
|
||||
* It's recommended when the caller wants to sort its own data structures to
|
||||
* align them with the order induced in the sparse set.
|
||||
*
|
||||
* The signature of the callback should be equivalent to the following:
|
||||
*
|
||||
* @code{.cpp}
|
||||
* bool(const Entity, const Entity);
|
||||
* @endcode
|
||||
*
|
||||
* @tparam Apply Type of function object to invoke to notify the caller.
|
||||
* @tparam Compare Type of comparison function object.
|
||||
* @tparam Sort Type of sort function object.
|
||||
* @tparam Args Types of arguments to forward to the sort function object.
|
||||
* @param first An iterator to the first element of the range to sort.
|
||||
* @param last An iterator past the last element of the range to sort.
|
||||
* @param apply A valid function object to use as a callback.
|
||||
* @param compare A valid comparison function object.
|
||||
* @param algo A valid sort function object.
|
||||
* @param args Arguments to forward to the sort function object, if any.
|
||||
*/
|
||||
template<typename Apply, typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void arrange(iterator_type first, iterator_type last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(last < first));
|
||||
ENTT_ASSERT(!(last > end()));
|
||||
|
||||
const auto length = std::distance(first, last);
|
||||
const auto skip = std::distance(last, end());
|
||||
const auto to = direct.rend() - skip;
|
||||
const auto from = to - length;
|
||||
|
||||
algo(from, to, std::move(compare), std::forward<Args>(args)...);
|
||||
|
||||
for(size_type pos = skip, end = skip+length; pos < end; pos++) {
|
||||
auto curr = pos;
|
||||
auto next = copy[curr];
|
||||
auto next = index(direct[curr]);
|
||||
|
||||
while(curr != next) {
|
||||
swap(direct[copy[curr] + offset], direct[copy[next] + offset]);
|
||||
copy[curr] = curr;
|
||||
auto [src_page, src_offset] = map(direct[curr]);
|
||||
auto [dst_page, dst_offset] = map(direct[next]);
|
||||
|
||||
apply(direct[curr], direct[next]);
|
||||
std::swap(reverse[src_page][src_offset], reverse[dst_page][dst_offset]);
|
||||
|
||||
curr = next;
|
||||
next = copy[curr];
|
||||
next = index(direct[curr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -426,10 +426,6 @@ public:
|
||||
* * An iterator past the last element of the range to sort.
|
||||
* * A comparison function to use to compare the elements.
|
||||
*
|
||||
* The comparison function object received by the sort function object
|
||||
* hasn't necessarily the type of the one passed along with the other
|
||||
* parameters to this member function.
|
||||
*
|
||||
* @note
|
||||
* Attempting to iterate elements using a raw pointer returned by a call to
|
||||
* either `data` or `raw` gives no guarantees on the order, even though
|
||||
@@ -446,19 +442,24 @@ public:
|
||||
*/
|
||||
template<typename Compare, typename Sort = std_sort, typename... Args>
|
||||
void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) {
|
||||
ENTT_ASSERT(!(first > last));
|
||||
ENTT_ASSERT(!(last < first));
|
||||
ENTT_ASSERT(!(last > end()));
|
||||
|
||||
const auto from = underlying_type::begin() + std::distance(begin(), first);
|
||||
const auto to = from + std::distance(first, last);
|
||||
|
||||
const auto apply = [this](const auto lhs, const auto rhs) {
|
||||
std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]);
|
||||
};
|
||||
|
||||
if constexpr(std::is_invocable_v<Compare, const object_type &, const object_type &>) {
|
||||
static_assert(!std::is_empty_v<object_type>);
|
||||
|
||||
underlying_type::sort(from, to, [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
|
||||
underlying_type::arrange(from, to, std::move(apply), [this, compare = std::move(compare)](const auto lhs, const auto rhs) {
|
||||
return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)]));
|
||||
}, std::move(algo), std::forward<Args>(args)...);
|
||||
} else {
|
||||
underlying_type::sort(from, to, std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
underlying_type::arrange(from, to, std::move(apply), std::move(compare), std::move(algo), std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
#include <iterator>
|
||||
#include <algorithm>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
#include <gtest/gtest.h>
|
||||
#include <entt/entity/sparse_set.hpp>
|
||||
@@ -240,9 +242,7 @@ TEST(SparseSet, SortOrdered) {
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
|
||||
|
||||
set.sort(set.begin(), set.end(), [](const auto lhs, const auto rhs) {
|
||||
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
|
||||
});
|
||||
set.sort(set.begin(), set.end(), std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
@@ -276,9 +276,7 @@ TEST(SparseSet, SortReverse) {
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.sort(set.begin(), set.end(), [](const auto lhs, const auto rhs) {
|
||||
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
|
||||
});
|
||||
set.sort(set.begin(), set.end(), std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
@@ -312,9 +310,7 @@ TEST(SparseSet, SortUnordered) {
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.sort(set.begin(), set.end(), [](const auto lhs, const auto rhs) {
|
||||
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
|
||||
});
|
||||
set.sort(set.begin(), set.end(), std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
@@ -348,9 +344,7 @@ TEST(SparseSet, SortRange) {
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.sort(set.end(), set.end(), [](const auto lhs, const auto rhs) {
|
||||
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
|
||||
});
|
||||
set.sort(set.end(), set.end(), std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
|
||||
@@ -358,9 +352,7 @@ TEST(SparseSet, SortRange) {
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.sort(set.begin(), set.begin(), [](const auto lhs, const auto rhs) {
|
||||
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
|
||||
});
|
||||
set.sort(set.begin(), set.begin(), std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
|
||||
@@ -368,9 +360,7 @@ TEST(SparseSet, SortRange) {
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.sort(set.begin()+2, set.begin()+3, [](const auto lhs, const auto rhs) {
|
||||
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
|
||||
});
|
||||
set.sort(set.begin()+2, set.begin()+3, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
|
||||
@@ -378,9 +368,7 @@ TEST(SparseSet, SortRange) {
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.sort(++set.begin(), --set.end(), [](const auto lhs, const auto rhs) {
|
||||
return std::underlying_type_t<entt::entity>(lhs) < std::underlying_type_t<entt::entity>(rhs);
|
||||
});
|
||||
set.sort(++set.begin(), --set.end(), std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
@@ -399,6 +387,104 @@ TEST(SparseSet, SortRange) {
|
||||
ASSERT_EQ(begin, end);
|
||||
}
|
||||
|
||||
TEST(SparseSet, ArrangOrdered) {
|
||||
entt::sparse_set<entt::entity> set;
|
||||
entt::entity entities[5]{entt::entity{42}, entt::entity{12}, entt::entity{9}, entt::entity{7}, entt::entity{3}};
|
||||
set.batch(std::begin(entities), std::end(entities));
|
||||
|
||||
set.arrange(set.begin(), set.end(), [](auto...) { FAIL(); }, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
|
||||
|
||||
ASSERT_TRUE(std::equal(std::begin(entities), std::end(entities), set.data()));
|
||||
}
|
||||
|
||||
TEST(SparseSet, ArrangeReverse) {
|
||||
entt::sparse_set<entt::entity> set;
|
||||
entt::entity entities[5]{entt::entity{3}, entt::entity{7}, entt::entity{9}, entt::entity{12}, entt::entity{42}};
|
||||
set.batch(std::begin(entities), std::end(entities));
|
||||
|
||||
set.arrange(set.begin(), set.end(), [&set, &entities](const auto lhs, const auto rhs) {
|
||||
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
|
||||
}, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
|
||||
|
||||
ASSERT_TRUE(std::equal(std::begin(entities), std::end(entities), set.data()));
|
||||
}
|
||||
|
||||
TEST(SparseSet, ArrangeUnordered) {
|
||||
entt::sparse_set<entt::entity> set;
|
||||
entt::entity entities[5]{entt::entity{9}, entt::entity{7}, entt::entity{3}, entt::entity{12}, entt::entity{42}};
|
||||
set.batch(std::begin(entities), std::end(entities));
|
||||
|
||||
set.arrange(set.begin(), set.end(), [&set, &entities](const auto lhs, const auto rhs) {
|
||||
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
|
||||
}, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{42});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 2u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{3});
|
||||
|
||||
ASSERT_TRUE(std::equal(std::begin(entities), std::end(entities), set.data()));
|
||||
}
|
||||
|
||||
TEST(SparseSet, ArrangeRange) {
|
||||
entt::sparse_set<entt::entity> set;
|
||||
entt::entity entities[5]{entt::entity{9}, entt::entity{7}, entt::entity{3}, entt::entity{12}, entt::entity{42}};
|
||||
set.batch(std::begin(entities), std::end(entities));
|
||||
|
||||
set.arrange(set.end(), set.end(), [&set, &entities](const auto lhs, const auto rhs) {
|
||||
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
|
||||
}, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.arrange(set.begin(), set.begin(), [&set, &entities](const auto lhs, const auto rhs) {
|
||||
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
|
||||
}, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.arrange(set.begin()+2, set.begin()+3, [&set, &entities](const auto lhs, const auto rhs) {
|
||||
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
|
||||
}, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 2u), entt::entity{3});
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
|
||||
set.arrange(++set.begin(), --set.end(), [&set, &entities](const auto lhs, const auto rhs) {
|
||||
std::swap(entities[set.index(lhs)], entities[set.index(rhs)]);
|
||||
}, std::less{});
|
||||
|
||||
ASSERT_EQ(*(set.data() + 0u), entt::entity{9});
|
||||
ASSERT_EQ(*(set.data() + 1u), entt::entity{12});
|
||||
ASSERT_EQ(*(set.data() + 2u), entt::entity{7});
|
||||
ASSERT_EQ(*(set.data() + 3u), entt::entity{3});
|
||||
ASSERT_EQ(*(set.data() + 4u), entt::entity{42});
|
||||
}
|
||||
|
||||
TEST(SparseSet, RespectDisjoint) {
|
||||
entt::sparse_set<entt::entity> lhs;
|
||||
entt::sparse_set<entt::entity> rhs;
|
||||
|
||||
Reference in New Issue
Block a user