From a32489a175d015201f7f217287b3531752c45bb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Tue, 8 Apr 2025 17:49:19 +0200 Subject: [PATCH 1/2] coins: introduce dynamic batch size calculator based on `dbcache` value The size scales linearly with `-dbcache` above the default 450 MiB, clamped between a minimum of `DEFAULT_DB_CACHE_BATCH` (16 MiB) and a maximum of 256 MiB. The minimum coincides with the default to prevent performance degradation for small caches and avoid discontinuities from integer division. This allows larger in-memory caches to use proportionally larger batch sizes, improving flush efficiency without impacting default configurations. Includes unit test coverage for key transition points. --- src/kernel/caches.h | 3 +++ src/node/coins_view_args.h | 14 +++++++++++++- src/test/coins_tests.cpp | 23 +++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/kernel/caches.h b/src/kernel/caches.h index 33ae604772b..1f2dab25dfb 100644 --- a/src/kernel/caches.h +++ b/src/kernel/caches.h @@ -16,6 +16,9 @@ static constexpr size_t MAX_BLOCK_DB_CACHE{2_MiB}; //! Max memory allocated to coin DB specific cache (bytes) static constexpr size_t MAX_COINS_DB_CACHE{8_MiB}; +//! The batch size of DEFAULT_KERNEL_CACHE +static constexpr size_t DEFAULT_DB_CACHE_BATCH{16_MiB}; + namespace kernel { struct CacheSizes { size_t block_tree_db; diff --git a/src/node/coins_view_args.h b/src/node/coins_view_args.h index 71a7a671fd8..441f3bf4b79 100644 --- a/src/node/coins_view_args.h +++ b/src/node/coins_view_args.h @@ -5,10 +5,22 @@ #ifndef BITCOIN_NODE_COINS_VIEW_ARGS_H #define BITCOIN_NODE_COINS_VIEW_ARGS_H +#include + class ArgsManager; struct CoinsViewOptions; -namespace node { +namespace node +{ +static constexpr size_t GetDbBatchSize(const size_t dbcache_bytes) +{ + return std::clamp( + (dbcache_bytes / DEFAULT_KERNEL_CACHE) * DEFAULT_DB_CACHE_BATCH, + /*lo=*/DEFAULT_DB_CACHE_BATCH, + /*hi=*/256_MiB + ); +} + void ReadCoinsViewArgs(const ArgsManager& args, CoinsViewOptions& options); } // namespace node diff --git a/src/test/coins_tests.cpp b/src/test/coins_tests.cpp index c46144b34b4..20e41428f15 100644 --- a/src/test/coins_tests.cpp +++ b/src/test/coins_tests.cpp @@ -20,6 +20,7 @@ #include #include +#include using namespace util::hex_literals; @@ -1074,4 +1075,26 @@ BOOST_AUTO_TEST_CASE(coins_resource_is_used) PoolResourceTester::CheckAllDataAccountedFor(resource); } +BOOST_AUTO_TEST_CASE(db_batch_sizes) +{ + BOOST_REQUIRE_EQUAL(node::GetDbBatchSize(DEFAULT_KERNEL_CACHE), DEFAULT_DB_CACHE_BATCH); + BOOST_REQUIRE_EQUAL(node::GetDbBatchSize(0_MiB), DEFAULT_DB_CACHE_BATCH); + + BOOST_CHECK_EQUAL(node::GetDbBatchSize(4_MiB), 16'777'216); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(10_MiB), 16'777'216); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(45_MiB), 16'777'216); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(100_MiB), 16'777'216); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(450_MiB), 16'777'216); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(1000_MiB), 33'554'432); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(2000_MiB), 67'108'864); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(3000_MiB), 100'663'296); + +#if SIZE_MAX > UINT32_MAX + BOOST_CHECK_EQUAL(node::GetDbBatchSize(4500_MiB), 167'772'160); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(7000_MiB), 251'658'240); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(10000_MiB), 268'435'456); + BOOST_CHECK_EQUAL(node::GetDbBatchSize(45000_MiB), 268'435'456); +#endif +} + BOOST_AUTO_TEST_SUITE_END() From 8fd522b223fc1405e70ca93c7e2d5a39f3f826fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Tue, 8 Apr 2025 17:50:48 +0200 Subject: [PATCH 2/2] coins: derive `batch_write_bytes` from `-dbcache` when unspecified Extend `ReadCoinsViewArgs` to dynamically set `batch_write_bytes` using `GetDbBatchSize()` when `-dbbatchsize` is not explicitly provided. This enables larger LevelDB batches on systems with high `-dbcache` values, which reduces the number of write operations during UTXO flushes and improves I/O efficiency, particularly during AssumeUTXO loads and IBD. --- src/init.cpp | 3 ++- src/node/coins_view_args.cpp | 8 ++++++-- src/txdb.h | 8 +++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/init.cpp b/src/init.cpp index a151c9d0a9e..5a791851c7c 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -487,7 +488,7 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc) argsman.AddArg("-coinstatsindex", strprintf("Maintain coinstats index used by the gettxoutsetinfo RPC (default: %u)", DEFAULT_COINSTATSINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-conf=", strprintf("Specify path to read-only configuration file. Relative paths will be prefixed by datadir location (only useable from command line, not configuration file) (default: %s)", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-datadir=", "Specify data directory", ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS); - argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: %u)", nDefaultDbBatchSize), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); + argsman.AddArg("-dbbatchsize", strprintf("Maximum database write batch size in bytes (default: calculated from the `-dbcache` value or %u if not set)", node::GetDbBatchSize(DEFAULT_DB_CACHE)), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS); argsman.AddArg("-dbcache=", strprintf("Maximum database cache size MiB (minimum %d, default: %d). Make sure you have enough RAM. In addition, unused memory allocated to the mempool is shared with this cache (see -maxmempool).", MIN_DB_CACHE >> 20, DEFAULT_DB_CACHE >> 20), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-includeconf=", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-allowignoredconf", strprintf("For backwards compatibility, treat an unused %s file in the datadir as a warning, not an error.", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); diff --git a/src/node/coins_view_args.cpp b/src/node/coins_view_args.cpp index 5d55143e832..2e513960dc7 100644 --- a/src/node/coins_view_args.cpp +++ b/src/node/coins_view_args.cpp @@ -2,6 +2,8 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + #include #include @@ -10,7 +12,9 @@ namespace node { void ReadCoinsViewArgs(const ArgsManager& args, CoinsViewOptions& options) { - if (auto value = args.GetIntArg("-dbbatchsize")) options.batch_write_bytes = *value; - if (auto value = args.GetIntArg("-dbcrashratio")) options.simulate_crash_ratio = *value; + if (const auto value = args.GetIntArg("-dbbatchsize")) options.batch_write_bytes = *value; + else options.batch_write_bytes = GetDbBatchSize(args.GetIntArg("-dbcache", DEFAULT_KERNEL_CACHE)); + + if (const auto value = args.GetIntArg("-dbcrashratio")) options.simulate_crash_ratio = *value; } } // namespace node diff --git a/src/txdb.h b/src/txdb.h index 968b7c27810..589dc5dceca 100644 --- a/src/txdb.h +++ b/src/txdb.h @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -21,16 +22,13 @@ class COutPoint; class uint256; -//! -dbbatchsize default (bytes) -static const int64_t nDefaultDbBatchSize = 16 << 20; - //! User-controlled performance and debug options. struct CoinsViewOptions { //! Maximum database write batch size in bytes. - size_t batch_write_bytes = nDefaultDbBatchSize; + size_t batch_write_bytes{DEFAULT_DB_CACHE_BATCH}; //! If non-zero, randomly exit when the database is flushed with (1/ratio) //! probability. - int simulate_crash_ratio = 0; + int simulate_crash_ratio{0}; }; /** CCoinsView backed by the coin database (chainstate/) */