sparse_set: better, faster range push

This commit is contained in:
Michele Caini
2022-12-16 18:45:51 +01:00
parent fbfde43477
commit 935393aae0
4 changed files with 93 additions and 44 deletions

View File

@@ -274,6 +274,27 @@ protected:
packed[static_cast<size_type>(entt)] = std::exchange(free_list, traits_type::combine(entt, tombstone));
}
/**
* @brief Assigns an entity to a sparse set.
* @param entt A valid identifier.
* @param force_back Force back insertion.
* @return Iterator pointing to the emplaced element.
*/
basic_iterator try_emplace(const Entity entt, const bool force_back) {
ENTT_ASSERT(!contains(entt), "Set already contains entity");
if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
packed.push_back(entt);
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
return begin();
} else {
const auto pos = static_cast<size_type>(traits_type::to_entity(free_list));
elem = traits_type::combine(traits_type::to_integral(free_list), traits_type::to_integral(entt));
free_list = std::exchange(packed[pos], entt);
return --(end() - pos);
}
}
protected:
/**
* @brief Erases entities from a sparse set.
@@ -293,24 +314,14 @@ protected:
}
/**
* @brief Assigns an entity to a sparse set.
* @param entt A valid identifier.
* @param force_back Force back insertion.
* @return Iterator pointing to the emplaced element.
* @brief Assigns one or more entities to a sparse set.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value Optional opaque value.
* @return Iterator pointing to the emplaced elements.
*/
virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) {
ENTT_ASSERT(!contains(entt), "Set already contains entity");
if(auto &elem = assure_at_least(entt); free_list == null || force_back) {
packed.push_back(entt);
elem = traits_type::combine(static_cast<typename traits_type::entity_type>(packed.size() - 1u), traits_type::to_integral(entt));
return begin();
} else {
const auto pos = static_cast<size_type>(traits_type::to_entity(free_list));
elem = traits_type::combine(traits_type::to_integral(free_list), traits_type::to_integral(entt));
free_list = std::exchange(packed[pos], entt);
return --(end() - pos);
}
virtual basic_iterator try_insert(basic_iterator first, [[maybe_unused]] basic_iterator last, [[maybe_unused]] const void *value) {
return first;
}
public:
@@ -690,7 +701,8 @@ public:
* `end()` iterator otherwise.
*/
iterator push(const entity_type entt, const void *value = nullptr) {
return try_emplace(entt, false, value);
const auto it = try_emplace(entt, false);
return try_insert(it, it + 1u, value);
}
/**
@@ -708,11 +720,27 @@ public:
*/
template<typename It>
iterator push(It first, It last) {
for(auto it = first; it != last; ++it) {
try_emplace(*it, true);
if(first == last) {
return end();
}
return first == last ? end() : find(*first);
const auto to = begin();
auto from = to;
ENTT_TRY {
for(; first != last; ++first) {
from = try_emplace(*first, true);
}
}
ENTT_CATCH {
for(; from != to; ++from) {
swap_and_pop(from);
}
ENTT_THROW;
}
return try_insert(from, to, nullptr);
}
/**

View File

@@ -264,9 +264,7 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
}
template<typename... Args>
auto emplace_element(const Entity entt, const bool force_back, Args &&...args) {
const auto it = base_type::try_emplace(entt, force_back);
void emplace_element(const underlying_iterator it, Args &&...args) {
ENTT_TRY {
auto elem = assure_at_least(static_cast<size_type>(it.index()));
entt::uninitialized_construct_using_allocator(to_address(elem), get_allocator(), std::forward<Args>(args)...);
@@ -275,8 +273,6 @@ class basic_storage: public basic_sparse_set<Entity, typename std::allocator_tra
base_type::pop(it, it + 1u);
ENTT_THROW;
}
return it;
}
void shrink_to_size(const std::size_t sz) {
@@ -350,23 +346,39 @@ protected:
}
/**
* @brief Assigns an entity to a storage.
* @param entt A valid identifier.
* @brief Assigns one or more entities to a sparse set.
* @param first An iterator to the first element of the range of entities.
* @param last An iterator past the last element of the range of entities.
* @param value Optional opaque value.
* @param force_back Force back insertion.
* @return Iterator pointing to the emplaced element.
* @return Iterator pointing to the emplaced elements.
*/
underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override {
underlying_iterator try_insert(underlying_iterator first, underlying_iterator last, const void *value = nullptr) override {
if(value) {
if constexpr(std::is_copy_constructible_v<value_type>) {
return emplace_element(entt, force_back, *static_cast<const value_type *>(value));
ENTT_ASSERT(std::next(first) == last, "Opaque emplace does not support ranges");
emplace_element(first, *static_cast<const value_type *>(value));
return first;
} else {
base_type::pop(first, last);
return base_type::end();
}
} else {
if constexpr(std::is_default_constructible_v<value_type>) {
return emplace_element(entt, force_back);
const auto placeholder = first;
ENTT_TRY {
for(; first != last; ++first) {
emplace_element(first);
}
return placeholder;
}
ENTT_CATCH {
base_type::pop(++first, last);
ENTT_THROW;
}
} else {
base_type::pop(first, last);
return base_type::end();
}
}
@@ -655,13 +667,15 @@ public:
*/
template<typename... Args>
value_type &emplace(const entity_type entt, Args &&...args) {
const auto it = base_type::try_emplace(entt, false);
if constexpr(std::is_aggregate_v<value_type>) {
const auto it = emplace_element(entt, false, Type{std::forward<Args>(args)...});
return element_at(static_cast<size_type>(it.index()));
emplace_element(it, Type{std::forward<Args>(args)...});
} else {
const auto it = emplace_element(entt, false, std::forward<Args>(args)...);
return element_at(static_cast<size_type>(it.index()));
emplace_element(it, std::forward<Args>(args)...);
}
return element_at(static_cast<size_type>(it.index()));
}
/**
@@ -695,7 +709,8 @@ public:
template<typename It>
void insert(It first, It last, const value_type &value = {}) {
for(; first != last; ++first) {
emplace_element(*first, true, value);
const auto it = base_type::try_emplace(*first, true);
emplace_element(it, value);
}
}
@@ -714,7 +729,8 @@ public:
template<typename EIt, typename CIt, typename = std::enable_if_t<std::is_same_v<typename std::iterator_traits<CIt>::value_type, value_type>>>
void insert(EIt first, EIt last, CIt from) {
for(; first != last; ++first, ++from) {
emplace_element(*first, true, *from);
const auto it = base_type::try_emplace(*first, true);
emplace_element(it, *from);
}
}

View File

@@ -39,10 +39,15 @@ class sigh_mixin final: public Type {
}
}
underlying_iterator try_emplace(const typename Type::entity_type entt, const bool force_back, const void *value) final {
underlying_iterator try_insert(underlying_iterator first, underlying_iterator last, const void *value = nullptr) override {
ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry");
Type::try_emplace(entt, force_back, value);
construction.publish(*owner, entt);
const auto entt = *first;
for(; first != last; ++first) {
Type::try_insert(first, first + 1u, value);
construction.publish(*owner, *first);
}
return Type::find(entt);
}

View File

@@ -1417,10 +1417,10 @@ TEST(SparseSet, ThrowingAllocator) {
ASSERT_THROW(set.push(std::begin(entities), std::end(entities)), test::throwing_allocator<entt::entity>::exception_type);
ASSERT_EQ(set.extent(), 2 * traits_type::page_size);
ASSERT_TRUE(set.contains(entt::entity{0}));
ASSERT_TRUE(set.contains(entt::entity{1}));
ASSERT_FALSE(set.contains(entt::entity{1}));
ASSERT_FALSE(set.contains(entt::entity{traits_type::page_size}));
ASSERT_EQ(set.capacity(), 2u);
ASSERT_EQ(set.size(), 2u);
ASSERT_EQ(set.size(), 1u);
set.push(entities[1u]);