In commit “Add allocator for node based containers” (96d8e33acaa079491403d8e8afa6709978dec205)
I still think it’s unfortunate to be adding this memusage
specialization when it is not necessary. I don’t like how it:
- Makes memusage.h depend node_allocator, when the job of memusage is just to tell us about memory usage of standard types, and it shouldn’t need to be referencing a particular specialized allocator.
- Redefines the concept of memory usage for this one unordered_map+node_allocator container to not consider memory allocated by the container to be “used” by the container, even though the container allocated that memory, and is still responsible for deallocating (if not freeing) it.
- Will require new specializations of map+node_allocator or list+node_allocator to be added here if node_allocator is used with other map or list containers in the future.
I think code can be simpler, definition of “usage” can be more consistent, and cyclic dependencies and special cases can be removed using earlier suggestions from #22702 (review), and only downside would be adding a counter variable. On top of the current PR, changes would look like:
0diff --git a/src/Makefile.am b/src/Makefile.am
1index b39d03fe8cb..9d09682e243 100644
2--- a/src/Makefile.am
3+++ b/src/Makefile.am
4@@ -217,7 +217,6 @@ BITCOIN_CORE_H = \
5 shutdown.h \
6 signet.h \
7 streams.h \
8- support/allocators/node_allocator/allocator_fwd.h \
9 support/allocators/node_allocator/allocator.h \
10 support/allocators/node_allocator/factory.h \
11 support/allocators/node_allocator/memory_resource.h \
12@@ -575,7 +574,6 @@ libbitcoin_util_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
13 libbitcoin_util_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
14 libbitcoin_util_a_SOURCES = \
15 support/lockedpool.cpp \
16- support/allocators/node_allocator/memory_resource.cpp \
17 chainparamsbase.cpp \
18 clientversion.cpp \
19 compat/glibcxx_sanity.cpp \
20diff --git a/src/memusage.h b/src/memusage.h
21index bc20ee4052c..b9faff9b2d9 100644
22--- a/src/memusage.h
23+++ b/src/memusage.h
24@@ -7,7 +7,6 @@
25
26 #include <indirectmap.h>
27 #include <prevector.h>
28-#include <support/allocators/node_allocator/allocator_fwd.h>
29 #include <support/allocators/node_allocator/node_size.h>
30
31 #include <stdlib.h>
32@@ -163,21 +162,13 @@ static inline size_t DynamicUsage(const std::unordered_set<X, Y>& s)
33 return MallocUsage(sizeof(unordered_node<X>)) * s.size() + MallocUsage(sizeof(void*) * s.bucket_count());
34 }
35
36-template <typename Key, typename Value, typename Hash, typename Equals>
37-static inline size_t DynamicUsage(const std::unordered_map<Key, Value, Hash, Equals>& m)
38+template <typename Key, typename Value, typename Hash, typename Equals, typename Alloc>
39+static inline size_t DynamicUsage(const std::unordered_map<Key, Value, Hash, Equals, Alloc>& m)
40 {
41- auto node_size = node_allocator::NodeSize<std::unordered_map<Key, Value, Hash, Equals>>::Value();
42+ auto node_size = node_allocator::NodeSize<std::unordered_map<Key, Value, Hash, Equals, Alloc>>::Value();
43 return MallocUsage(node_size) * m.size() + MallocUsage(sizeof(void*) * m.bucket_count());
44 }
45
46-template <typename Key, typename Value, typename Hash, typename Equals, typename AllocT, size_t AllocS>
47-static inline size_t DynamicUsage(const std::unordered_map<Key, Value, Hash, Equals, node_allocator::Allocator<AllocT, AllocS>>& m)
48-{
49- // Since node_allocator is used, the memory of the nodes are the MemoryResource's responsibility and
50- // won't be added here. Also multiple maps could use the same MemoryResource.
51- return MallocUsage(sizeof(void*) * m.bucket_count());
52-}
53-
54 }
55
56 #endif // BITCOIN_MEMUSAGE_H
57diff --git a/src/support/allocators/node_allocator/allocator.h b/src/support/allocators/node_allocator/allocator.h
58index 778471bb20b..c728ab8638a 100644
59--- a/src/support/allocators/node_allocator/allocator.h
60+++ b/src/support/allocators/node_allocator/allocator.h
61@@ -5,7 +5,6 @@
62 #ifndef BITCOIN_SUPPORT_ALLOCATORS_NODE_ALLOCATOR_ALLOCATOR_H
63 #define BITCOIN_SUPPORT_ALLOCATORS_NODE_ALLOCATOR_ALLOCATOR_H
64
65-#include <support/allocators/node_allocator/allocator_fwd.h>
66 #include <support/allocators/node_allocator/memory_resource.h>
67
68 #include <cstdint>
69diff --git a/src/support/allocators/node_allocator/allocator_fwd.h b/src/support/allocators/node_allocator/allocator_fwd.h
70deleted file mode 100644
71index e3fa0ddd889..00000000000
72--- a/src/support/allocators/node_allocator/allocator_fwd.h
73+++ /dev/null
74@@ -1,17 +0,0 @@
75-// Copyright (c) 2021 The Bitcoin Core developers
76-// Distributed under the MIT software license, see the accompanying
77-// file COPYING or http://www.opensource.org/licenses/mit-license.php.
78-
79-#ifndef BITCOIN_SUPPORT_ALLOCATORS_NODE_ALLOCATOR_ALLOCATOR_FWD_H
80-#define BITCOIN_SUPPORT_ALLOCATORS_NODE_ALLOCATOR_ALLOCATOR_FWD_H
81-
82-#include <cstddef>
83-
84-namespace node_allocator {
85-
86-template <typename T, size_t ALLOCATION_SIZE_BYTES>
87-class Allocator;
88-
89-} // namespace node_allocator
90-
91-#endif // BITCOIN_SUPPORT_ALLOCATORS_NODE_ALLOCATOR_ALLOCATOR_FWD_H
92diff --git a/src/support/allocators/node_allocator/memory_resource.cpp b/src/support/allocators/node_allocator/memory_resource.cpp
93deleted file mode 100644
94index 368752fa2b9..00000000000
95--- a/src/support/allocators/node_allocator/memory_resource.cpp
96+++ /dev/null
97@@ -1,12 +0,0 @@
98-#include <support/allocators/node_allocator/memory_resource.h>
99-
100-#include <memusage.h>
101-
102-namespace node_allocator::detail {
103-
104-size_t DynamicMemoryUsage(size_t pool_size_bytes, std::vector<std::unique_ptr<char[]>> const& allocated_pools)
105-{
106- return memusage::MallocUsage(pool_size_bytes) * allocated_pools.size() + memusage::DynamicUsage(allocated_pools);
107-}
108-
109-} // namespace node_allocator::detail
110diff --git a/src/support/allocators/node_allocator/memory_resource.h b/src/support/allocators/node_allocator/memory_resource.h
111index f4ba0138924..e60e847c86d 100644
112--- a/src/support/allocators/node_allocator/memory_resource.h
113+++ b/src/support/allocators/node_allocator/memory_resource.h
114@@ -8,6 +8,8 @@
115 #include <utility>
116 #include <vector>
117
118+#include <memusage.h>
119+
120 #ifndef BITCOIN_SUPPORT_ALLOCATORS_NODE_ALLOCATOR_MEMORY_RESOURCE_H
121 #define BITCOIN_SUPPORT_ALLOCATORS_NODE_ALLOCATOR_MEMORY_RESOURCE_H
122
123@@ -38,12 +40,6 @@ template <typename T>
124 return ((size_max + alignment_max - 1U) / alignment_max) * alignment_max;
125 }
126
127-/**
128- * Calculates dynamic memory usage of a MemoryResource. Not a member of MemoryResource so we can implement this
129- * in a cpp and don't depend on memusage.h in the header.
130- */
131-[[nodiscard]] size_t DynamicMemoryUsage(size_t pool_size_bytes, std::vector<std::unique_ptr<char[]>> const& allocated_pools);
132-
133 } // namespace detail
134
135 /**
136@@ -102,6 +98,7 @@ public:
137 {
138 static_assert(ALLOCATION_SIZE_BYTES == detail::RequiredAllocationSizeBytes<T>());
139 static_assert(__STDCPP_DEFAULT_NEW_ALIGNMENT__ >= std::alignment_of_v<T>, "make sure AllocatePool() aligns correctly");
140+ ++m_allocations;
141
142 if (m_free_allocations) {
143 // we've already got data in the free list, unlink one element
144@@ -138,6 +135,7 @@ public:
145 void Deallocate(void* p) noexcept
146 {
147 static_assert(ALLOCATION_SIZE_BYTES == detail::RequiredAllocationSizeBytes<T>());
148+ --m_allocations;
149
150 // put it into the linked list.
151 //
152@@ -150,11 +148,11 @@ public:
153 }
154
155 /**
156- * Calculates bytes allocated by the memory resource.
157+ * Calculates number of bytes allocated to the memory resource minus number of bytes allocated from the memory resource.
158 */
159 [[nodiscard]] size_t DynamicMemoryUsage() const noexcept
160 {
161- return detail::DynamicMemoryUsage(POOL_SIZE_BYTES, m_allocated_pools);
162+ return memusage::MallocUsage(POOL_SIZE_BYTES) * m_allocated_pools.size() + memusage::DynamicUsage(m_allocated_pools) - ALLOCATION_SIZE_BYTES * m_allocations;
163 }
164
165 private:
166@@ -171,6 +169,9 @@ private:
167 m_untouched_memory_end = m_untouched_memory_iterator + POOL_SIZE_BYTES;
168 }
169
170+ //! Number of current allocations.
171+ size_t m_allocations = 0;
172+
173 //! Contains all allocated pools of memory, used to free the data in the destructor.
174 std::vector<std::unique_ptr<char[]>> m_allocated_pools{};
175