From 521110503d56da0d37e8d75cb982d2fc07e3b7d5 Mon Sep 17 00:00:00 2001 From: Eric Voskuil Date: Thu, 3 Sep 2026 21:35:13 -0400 Subject: [PATCH] Derive hashmap head buckets from RAM and expected count. --- include/bitcoin/database/memory/utilities.hpp | 24 ++++----- src/memory/utilities.cpp | 51 +++++++++++++++---- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/include/bitcoin/database/memory/utilities.hpp b/include/bitcoin/database/memory/utilities.hpp index 8f273f26d..22a8b84d7 100644 --- a/include/bitcoin/database/memory/utilities.hpp +++ b/include/bitcoin/database/memory/utilities.hpp @@ -33,6 +33,12 @@ BCD_API size_t page_size() NOEXCEPT; /// The bytes of physical memory, zero if failed. BCD_API uint64_t system_memory() NOEXCEPT; +/// Head buckets from expected elements and installed memory, scaled linearly +/// between the contested and uncontested load factors (in tenths). Hashmaps +/// only; zero expected is not derived. +BCD_API uint32_t derive_buckets(uint64_t expected, uint32_t low, + uint32_t high) NOEXCEPT; + /// The bytes of unused physical memory, zero if failed. Scarcity precedes /// pressure: clean file cache is reclaimable, so the pressure level does not /// raise while free memory exhausts. @@ -51,10 +57,12 @@ BCD_API size_t system_pressure() NOEXCEPT; /// if failed or the platform provides no source. BCD_API uint64_t system_compressed() NOEXCEPT; +// TODO: move all below to release.hpp, remove compound ternaries, delint. + /// Head-release page-run helpers (pure, unit tested). Pages are tracked as /// bits in 64-bit words (low order bit is the lowest page of the word). A /// release candidate page is clean (not dirty), cold (not recently written) -/// and not already released; candidacy is limited to full pages below the +/// and not already released. Candidacy is limited to full pages below the /// logical page count. /// --------------------------------------------------------------------------- @@ -134,20 +142,6 @@ inline std::pair bit_run(const uint64_t* words, size_t pages, return { start, end }; } -/// C++26: std::atomic::fetch_max -template = true> -Integral fetch_max(std::atomic& atomic, Integral value) NOEXCEPT -{ - constexpr auto relaxed = std::memory_order_relaxed; - - auto atom = atomic.load(relaxed); - while (value > atom && !atomic.compare_exchange_weak(atom, value, relaxed)) - { - } - - return atom; -} - } // namespace database } // namespace libbitcoin diff --git a/src/memory/utilities.cpp b/src/memory/utilities.cpp index fcf201eb0..6cdeae1f4 100644 --- a/src/memory/utilities.cpp +++ b/src/memory/utilities.cpp @@ -37,15 +37,16 @@ namespace libbitcoin { namespace database { +using namespace system; + #if defined(HAVE_MSC) size_t page_size() NOEXCEPT { - using namespace system; SYSTEM_INFO info{}; ::GetSystemInfo(&info); - BC_ASSERT(!system::is_limited(info.dwPageSize)); + BC_ASSERT(!is_limited(info.dwPageSize)); return info.dwPageSize; } @@ -94,12 +95,11 @@ uint64_t system_compressed() NOEXCEPT return zero; } -#else +#else // HAVE_MSC size_t page_size() NOEXCEPT { errno = 0; - using namespace system; const auto size = sysconf(_SC_PAGESIZE); if (is_zero(errno) && !is_negative(size)) { @@ -113,7 +113,6 @@ size_t page_size() NOEXCEPT uint64_t system_memory() NOEXCEPT { errno = 0; - using namespace system; const int64_t pages = sysconf(_SC_PHYS_PAGES); if (is_zero(errno) && !is_negative(pages)) { @@ -128,7 +127,6 @@ uint64_t system_memory() NOEXCEPT uint64_t system_free() NOEXCEPT { #if defined(HAVE_APPLE) - using namespace system; auto count = HOST_VM_INFO64_COUNT; vm_statistics64_data_t statistics{}; if (::host_statistics64(::mach_host_self(), HOST_VM_INFO64, @@ -138,7 +136,6 @@ uint64_t system_free() NOEXCEPT } #else errno = 0; - using namespace system; const int64_t pages = sysconf(_SC_AVPHYS_PAGES); if (is_zero(errno) && !is_negative(pages)) { @@ -157,7 +154,6 @@ uint64_t system_available() NOEXCEPT #if defined(HAVE_APPLE) // Free plus purgeable plus file-backed (external) pages are available to // allocation without compression or swap. - using namespace system; auto count = HOST_VM_INFO64_COUNT; vm_statistics64_data_t statistics{}; if (::host_statistics64(::mach_host_self(), HOST_VM_INFO64, @@ -180,7 +176,7 @@ uint64_t system_available() NOEXCEPT &kilobytes) == 1) { std::fclose(file); - return system::ceilinged_multiply(kilobytes, 1024u); + return ceilinged_multiply(kilobytes, 1024u); } } @@ -200,7 +196,7 @@ size_t system_pressure() NOEXCEPT if (is_zero(::sysctlbyname("kern.memorystatus_vm_pressure_level", &level, &size, nullptr, 0))) { - return system::possible_narrow_sign_cast(level); + return possible_narrow_sign_cast(level); } #elif defined(HAVE_LINUX) // PSI (requires CONFIG_PSI): fraction of recent wall time that tasks @@ -235,7 +231,6 @@ size_t system_pressure() NOEXCEPT uint64_t system_compressed() NOEXCEPT { #if defined(HAVE_APPLE) - using namespace system; auto count = HOST_VM_INFO64_COUNT; vm_statistics64_data_t statistics{}; if (::host_statistics64(::mach_host_self(), HOST_VM_INFO64, @@ -251,7 +246,41 @@ uint64_t system_compressed() NOEXCEPT return zero; } +#endif // HAVE_MSC + +constexpr auto gigabyte = power2(30u); +constexpr auto megabyte = power2(20u); +constexpr auto uncontested = power2(35u); + +#if defined(HAVE_APPLE) +constexpr auto contested = 10u * gigabyte; +#else +constexpr auto contested = 8u * gigabyte; #endif +uint32_t derive_buckets(uint64_t expected, uint32_t low, + uint32_t high) NOEXCEPT +{ + if (is_zero(expected) || is_zero(low) || is_zero(high)) + return {}; + + const auto scaled = ceilinged_multiply(expected, 10); + const auto floored = scaled / low; + const auto ceiled = scaled / high; + const auto memory = system_memory(); + + if (memory <= contested) + return possible_narrow_cast(floored); + + if (memory >= uncontested) + return possible_narrow_cast(ceiled); + + // Megabytes, as the byte product overflows the word. + const auto over = (memory - contested) / megabyte; + const auto rise = floored_subtract(ceiled, floored); + constexpr auto span = (uncontested - contested) / megabyte; + return limit(floored + (ceilinged_multiply(rise, over) / span)); +} + } // namespace database } // namespace libbitcoin