Răsfoiți Sursa

sparse_set: used compressed pair to hide the cost of storing allocators

Michele Caini 4 ani în urmă
părinte
comite
fbc9595f12
1 a modificat fișierele cu 27 adăugiri și 30 ștergeri
  1. 27 30
      src/entt/entity/sparse_set.hpp

+ 27 - 30
src/entt/entity/sparse_set.hpp

@@ -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;
 };