sparse_set: used compressed pair to hide the cost of storing allocators
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
#include <utility>
|
||||
#include "../config/config.h"
|
||||
#include "../core/algorithm.hpp"
|
||||
#include "../core/compressed_pair.hpp"
|
||||
#include "../core/fwd.hpp"
|
||||
#include "entity.hpp"
|
||||
#include "fwd.hpp"
|
||||
@@ -65,9 +66,6 @@ class basic_sparse_set {
|
||||
using alloc_ptr_traits = typename std::allocator_traits<alloc_ptr>;
|
||||
using alloc_ptr_pointer = typename alloc_ptr_traits::pointer;
|
||||
|
||||
// TODO using bucket_alloc_traits = typename std::allocator_traits<Allocator>::template rebind_traits<alloc_pointer>;
|
||||
// TODO using bucket_alloc_pointer = typename bucket_alloc_traits::pointer;
|
||||
|
||||
using entity_traits = entt_traits<Entity>;
|
||||
|
||||
static_assert(alloc_traits::propagate_on_container_move_assignment::value);
|
||||
@@ -181,7 +179,7 @@ class basic_sparse_set {
|
||||
[[nodiscard]] auto assure_page(const std::size_t idx) {
|
||||
if(!(idx < bucket)) {
|
||||
const size_type sz = idx + 1u;
|
||||
alloc_ptr allocator_ptr{allocator};
|
||||
alloc_ptr allocator_ptr{reserved.first()};
|
||||
const auto mem = alloc_ptr_traits::allocate(allocator_ptr, sz);
|
||||
|
||||
std::uninitialized_value_construct(mem + bucket, mem + sz);
|
||||
@@ -195,7 +193,7 @@ class basic_sparse_set {
|
||||
}
|
||||
|
||||
if(!sparse[idx]) {
|
||||
sparse[idx] = alloc_traits::allocate(allocator, sparse_page);
|
||||
sparse[idx] = alloc_traits::allocate(reserved.first(), sparse_page);
|
||||
std::uninitialized_fill(sparse[idx], sparse[idx] + sparse_page, null);
|
||||
}
|
||||
|
||||
@@ -203,21 +201,24 @@ class basic_sparse_set {
|
||||
}
|
||||
|
||||
void resize_packed(const std::size_t req) {
|
||||
ENTT_ASSERT((req != reserved) && !(req < count), "Invalid request");
|
||||
auto &&[allocator, len] = reserved;
|
||||
ENTT_ASSERT((req != len) && !(req < count), "Invalid request");
|
||||
const auto mem = alloc_traits::allocate(allocator, req);
|
||||
|
||||
std::uninitialized_copy(packed, packed + count, mem);
|
||||
std::uninitialized_fill(mem + count, mem + req, tombstone);
|
||||
|
||||
std::destroy(packed, packed + reserved);
|
||||
alloc_traits::deallocate(allocator, packed, reserved);
|
||||
std::destroy(packed, packed + len);
|
||||
alloc_traits::deallocate(allocator, packed, len);
|
||||
|
||||
packed = mem;
|
||||
reserved = req;
|
||||
len = req;
|
||||
}
|
||||
|
||||
void release_memory() {
|
||||
if(packed) {
|
||||
auto &&[allocator, len] = reserved;
|
||||
|
||||
for(size_type pos{}; pos < bucket; ++pos) {
|
||||
if(sparse[pos]) {
|
||||
std::destroy(sparse[pos], sparse[pos] + sparse_page);
|
||||
@@ -225,11 +226,11 @@ class basic_sparse_set {
|
||||
}
|
||||
}
|
||||
|
||||
std::destroy(packed, packed + reserved);
|
||||
std::destroy(packed, packed + len);
|
||||
std::destroy(sparse, sparse + bucket);
|
||||
|
||||
alloc_ptr allocator_ptr{allocator};
|
||||
alloc_traits::deallocate(allocator, packed, reserved);
|
||||
alloc_traits::deallocate(allocator, packed, len);
|
||||
alloc_ptr_traits::deallocate(allocator_ptr, sparse, bucket);
|
||||
}
|
||||
}
|
||||
@@ -303,12 +304,11 @@ public:
|
||||
* @param alloc Allocator to use (possibly default-constructed).
|
||||
*/
|
||||
explicit basic_sparse_set(deletion_policy pol, const allocator_type &alloc = {})
|
||||
: allocator{alloc},
|
||||
sparse{alloc_ptr_traits::allocate(alloc_ptr{allocator}, 0u)},
|
||||
packed{alloc_traits::allocate(allocator, 0u)},
|
||||
: reserved{alloc, 0u},
|
||||
sparse{alloc_ptr_traits::allocate(alloc_ptr{reserved.first()}, 0u)},
|
||||
packed{alloc_traits::allocate(reserved.first(), 0u)},
|
||||
bucket{0u},
|
||||
count{0u},
|
||||
reserved{0u},
|
||||
free_list{tombstone},
|
||||
mode{pol}
|
||||
{}
|
||||
@@ -326,12 +326,11 @@ public:
|
||||
* @param other The instance to move from.
|
||||
*/
|
||||
basic_sparse_set(basic_sparse_set &&other) ENTT_NOEXCEPT
|
||||
: allocator{std::move(other.allocator)},
|
||||
: reserved{std::move(other.reserved)},
|
||||
sparse{std::exchange(other.sparse, alloc_ptr_pointer{})},
|
||||
packed{std::exchange(other.packed, alloc_pointer{})},
|
||||
bucket{std::exchange(other.bucket, 0u)},
|
||||
count{std::exchange(other.count, 0u)},
|
||||
reserved{std::exchange(other.reserved, 0u)},
|
||||
free_list{std::exchange(other.free_list, tombstone)},
|
||||
mode{other.mode}
|
||||
{}
|
||||
@@ -349,12 +348,11 @@ public:
|
||||
basic_sparse_set & operator=(basic_sparse_set &&other) ENTT_NOEXCEPT {
|
||||
release_memory();
|
||||
|
||||
allocator = std::move(other.allocator);
|
||||
reserved = std::move(other.reserved);
|
||||
sparse = std::exchange(other.sparse, alloc_ptr_pointer{});
|
||||
packed = std::exchange(other.packed, alloc_pointer{});
|
||||
bucket = std::exchange(other.bucket, 0u);
|
||||
count = std::exchange(other.count, 0u);
|
||||
reserved = std::exchange(other.reserved, 0u);
|
||||
free_list = std::exchange(other.free_list, tombstone);
|
||||
mode = other.mode;
|
||||
|
||||
@@ -366,7 +364,7 @@ public:
|
||||
* @return The associated allocator.
|
||||
*/
|
||||
[[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT {
|
||||
return allocator_type{allocator};
|
||||
return allocator_type{reserved.first()};
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -394,7 +392,7 @@ public:
|
||||
* @param cap Desired capacity.
|
||||
*/
|
||||
void reserve(const size_type cap) {
|
||||
if(cap > reserved) {
|
||||
if(cap > reserved.second()) {
|
||||
resize_packed(cap);
|
||||
}
|
||||
}
|
||||
@@ -405,12 +403,12 @@ public:
|
||||
* @return Capacity of the sparse set.
|
||||
*/
|
||||
[[nodiscard]] size_type capacity() const ENTT_NOEXCEPT {
|
||||
return reserved;
|
||||
return reserved.second();
|
||||
}
|
||||
|
||||
/*! @brief Requests the removal of unused capacity. */
|
||||
void shrink_to_fit() {
|
||||
if(count < reserved) {
|
||||
if(count < reserved.second()) {
|
||||
resize_packed(count);
|
||||
}
|
||||
}
|
||||
@@ -583,9 +581,9 @@ public:
|
||||
size_type emplace_back(const entity_type entt) {
|
||||
ENTT_ASSERT(!contains(entt), "Set already contains entity");
|
||||
|
||||
if(count == reserved) {
|
||||
const size_type sz = static_cast<size_type>(reserved * growth_factor);
|
||||
resize_packed(sz + !(sz > reserved));
|
||||
if(const auto len = reserved.second(); count == len) {
|
||||
const size_type sz = static_cast<size_type>(len * growth_factor);
|
||||
resize_packed(sz + !(sz > len));
|
||||
}
|
||||
|
||||
assure_page(page(entt))[offset(entt)] = entity_traits::construct(static_cast<typename entity_traits::entity_type>(count));
|
||||
@@ -867,12 +865,11 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
alloc allocator;
|
||||
compressed_pair<alloc, size_type> reserved;
|
||||
alloc_ptr_pointer sparse;
|
||||
alloc_pointer packed;
|
||||
std::size_t bucket;
|
||||
std::size_t count;
|
||||
std::size_t reserved;
|
||||
size_type bucket;
|
||||
size_type count;
|
||||
entity_type free_list;
|
||||
deletion_policy mode;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user