improve MallocUsage() accuracy

Co-authored-by: Pieter Wuille <pieter@wuille.net>
Co-authored-by: Martin Leitner-Ankerl <martin.ankerl@gmail.com>
This commit is contained in:
Larry Ruane 2023-09-25 14:14:39 -06:00
parent 9c89fb4cc1
commit 1fcbebcae0
2 changed files with 30 additions and 13 deletions

View file

@ -47,19 +47,31 @@ template<typename X> static inline size_t DynamicUsage(const X * const &v) { ret
* application data structures require more accurate inner accounting, they should * application data structures require more accurate inner accounting, they should
* iterate themselves, or use more efficient caching + updating on modification. * iterate themselves, or use more efficient caching + updating on modification.
*/ */
static inline size_t MallocUsage(size_t alloc) static inline size_t MallocUsage(size_t alloc)
{ {
// Measured on libc6 2.19 on Linux. // There are few if any actual zero-length allocations; when
if (alloc == 0) { // DynamicUsage(std::vector<X>& v) calls this function, and v.capacity() == 0,
return 0; // for example, there has not been a zero-byte allocation (which would require
} else if (sizeof(void*) == 8) { // some physical space). std::vector has optimized this case. Experimental
return ((alloc + 31) >> 4) << 4; // evidence indicates the same is true of other data structures -- there are
} else if (sizeof(void*) == 4) { // no actual zero-length allocations observed, although of course this is
return ((alloc + 15) >> 3) << 3; // library-dependent.
} else { if (alloc == 0) return 0;
assert(0);
} static constexpr size_t overhead = sizeof(void*);
#if defined(__arm__)
// tested with ARM 32bit
static constexpr size_t step = sizeof(void*) * 2;
static constexpr size_t min_alloc = 9;
#else
static constexpr size_t step = 16U;
static constexpr size_t min_alloc = sizeof(void*) == 8 ? 9 : 0;
#endif
// step should be nonzero and a power of 2
static_assert(step > 0);
static_assert((step & (step-1)) == 0);
// tested with Linux glibc 2.31 and 2.38, 32bit and 64bit
return (std::max(min_alloc, alloc) + overhead + (step - 1)) & ~(step - 1);
} }
// STL data structures // STL data structures
@ -164,11 +176,16 @@ static inline size_t DynamicUsage(const std::list<X>& l)
return MallocUsage(sizeof(list_node<X>)) * l.size(); return MallocUsage(sizeof(list_node<X>)) * l.size();
} }
// struct unordered_node adds the container overhead to the given structure X,
// although this varies across library container implementations (platforms).
// It is believed that some containers use two pointers per node, so this
// generates a conservative memory usage estimate (it may be slightly too high).
template<typename X> template<typename X>
struct unordered_node : private X struct unordered_node : private X
{ {
private: private:
void* ptr; void* ptr_back;
void* ptr_forward;
}; };
template<typename X, typename Y> template<typename X, typename Y>

View file

@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
// (prevector<28, unsigned char>) when assigned 56 bytes of data per above. // (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
// //
// See also: Coin::DynamicMemoryUsage(). // See also: Coin::DynamicMemoryUsage().
constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64; constexpr size_t COIN_SIZE{64};
auto print_view_mem_usage = [](CCoinsViewCache& view) { auto print_view_mem_usage = [](CCoinsViewCache& view) {
BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage()); BOOST_TEST_MESSAGE("CCoinsViewCache memory usage: " << view.DynamicMemoryUsage());