kernel: Move kernel-specific cache size options to kernel

Carrying non-kernel related fields in the cache sizes for the indexes is
confusing for kernel library users. The cache sizes also are set
currently with magic numbers in bitcoin-chainstate. The comments for the
cache size calculations are also not completely clear.

Solve these things by moving the kernel-specific cache size fields to
their own struct.

This slightly changes the way the cache is allocated if the txindex
and/or blockfilterindex is used. Since they are now given precedence
over the block tree db cache, this results in a bit less cache being
allocated to the block tree db, coinsdb and coins caches. The effect is
negligible though, i.e. cache sizes with default dbcache reported
through the logs are:

master:
Cache configuration:
* Using 2.0 MiB for block index database
* Using 56.0 MiB for transaction index database
* Using 49.0 MiB for basic block filter index database
* Using 8.0 MiB for chain state database
* Using 335.0 MiB for in-memory UTXO set (plus up to 286.1 MiB of unused mempool space)

this branch:
Cache configuration:
* Using 2.0 MiB for block index database
* Using 56.2 MiB for transaction index database
* Using 49.2 MiB for basic block filter index database
* Using 8.0 MiB for chain state database
* Using 334.5 MiB for in-memory UTXO set (plus up to 286.1 MiB of unused mempool space)
This commit is contained in:
TheCharlatan 2024-11-22 20:51:34 +01:00
parent d5e2c4a409
commit e758b26b85
No known key found for this signature in database
GPG key ID: 9B79B45691DB4173
10 changed files with 74 additions and 44 deletions

View file

@ -123,10 +123,7 @@ int main(int argc, char* argv[])
util::SignalInterrupt interrupt; util::SignalInterrupt interrupt;
ChainstateManager chainman{interrupt, chainman_opts, blockman_opts}; ChainstateManager chainman{interrupt, chainman_opts, blockman_opts};
node::CacheSizes cache_sizes; kernel::CacheSizes cache_sizes{nDefaultDbCache << 20};
cache_sizes.block_tree_db = 2 << 20;
cache_sizes.coins_db = 2 << 22;
cache_sizes.coins = (450 << 20) - (2 << 20) - (2 << 22);
node::ChainstateLoadOptions options; node::ChainstateLoadOptions options;
auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options); auto [status, error] = node::LoadChainstate(chainman, cache_sizes, options);
if (status != node::ChainstateLoadStatus::SUCCESS) { if (status != node::ChainstateLoadStatus::SUCCESS) {

View file

@ -32,6 +32,7 @@
#include <interfaces/ipc.h> #include <interfaces/ipc.h>
#include <interfaces/mining.h> #include <interfaces/mining.h>
#include <interfaces/node.h> #include <interfaces/node.h>
#include <kernel/caches.h>
#include <kernel/context.h> #include <kernel/context.h>
#include <key.h> #include <key.h>
#include <logging.h> #include <logging.h>
@ -121,7 +122,6 @@ using common::ResolveErrMsg;
using node::ApplyArgsManOptions; using node::ApplyArgsManOptions;
using node::BlockManager; using node::BlockManager;
using node::CacheSizes;
using node::CalculateCacheSizes; using node::CalculateCacheSizes;
using node::ChainstateLoadResult; using node::ChainstateLoadResult;
using node::ChainstateLoadStatus; using node::ChainstateLoadStatus;
@ -1177,7 +1177,7 @@ static ChainstateLoadResult InitAndLoadChainstate(
NodeContext& node, NodeContext& node,
bool do_reindex, bool do_reindex,
const bool do_reindex_chainstate, const bool do_reindex_chainstate,
CacheSizes& cache_sizes, const kernel::CacheSizes& cache_sizes,
const ArgsManager& args) const ArgsManager& args)
{ {
const CChainParams& chainparams = Params(); const CChainParams& chainparams = Params();
@ -1603,18 +1603,18 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
ReadNotificationArgs(args, kernel_notifications); ReadNotificationArgs(args, kernel_notifications);
// cache size calculations // cache size calculations
CacheSizes cache_sizes = CalculateCacheSizes(args, g_enabled_filter_types.size()); const auto [index_cache_sizes, kernel_cache_sizes] = CalculateCacheSizes(args, g_enabled_filter_types.size());
LogPrintf("Cache configuration:\n"); LogInfo("Cache configuration:");
LogPrintf("* Using %.1f MiB for block index database\n", cache_sizes.block_tree_db * (1.0 / 1024 / 1024)); LogInfo("* Using %.1f MiB for block index database", kernel_cache_sizes.block_tree_db * (1.0 / 1024 / 1024));
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
LogPrintf("* Using %.1f MiB for transaction index database\n", cache_sizes.tx_index * (1.0 / 1024 / 1024)); LogInfo("* Using %.1f MiB for transaction index database", index_cache_sizes.tx_index * (1.0 / 1024 / 1024));
} }
for (BlockFilterType filter_type : g_enabled_filter_types) { for (BlockFilterType filter_type : g_enabled_filter_types) {
LogPrintf("* Using %.1f MiB for %s block filter index database\n", LogInfo("* Using %.1f MiB for %s block filter index database",
cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type)); index_cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
} }
LogPrintf("* Using %.1f MiB for chain state database\n", cache_sizes.coins_db * (1.0 / 1024 / 1024)); LogInfo("* Using %.1f MiB for chain state database", kernel_cache_sizes.coins_db * (1.0 / 1024 / 1024));
assert(!node.mempool); assert(!node.mempool);
assert(!node.chainman); assert(!node.chainman);
@ -1627,7 +1627,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
node, node,
do_reindex, do_reindex,
do_reindex_chainstate, do_reindex_chainstate,
cache_sizes, kernel_cache_sizes,
args); args);
if (status == ChainstateLoadStatus::FAILURE && !do_reindex && !ShutdownRequested(node)) { if (status == ChainstateLoadStatus::FAILURE && !do_reindex && !ShutdownRequested(node)) {
// suggest a reindex // suggest a reindex
@ -1646,7 +1646,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
node, node,
do_reindex, do_reindex,
do_reindex_chainstate, do_reindex_chainstate,
cache_sizes, kernel_cache_sizes,
args); args);
} }
if (status != ChainstateLoadStatus::SUCCESS && status != ChainstateLoadStatus::INTERRUPTED) { if (status != ChainstateLoadStatus::SUCCESS && status != ChainstateLoadStatus::INTERRUPTED) {
@ -1672,12 +1672,12 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// ********************************************************* Step 8: start indexers // ********************************************************* Step 8: start indexers
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) { if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
g_txindex = std::make_unique<TxIndex>(interfaces::MakeChain(node), cache_sizes.tx_index, false, do_reindex); g_txindex = std::make_unique<TxIndex>(interfaces::MakeChain(node), index_cache_sizes.tx_index, false, do_reindex);
node.indexes.emplace_back(g_txindex.get()); node.indexes.emplace_back(g_txindex.get());
} }
for (const auto& filter_type : g_enabled_filter_types) { for (const auto& filter_type : g_enabled_filter_types) {
InitBlockFilterIndex([&]{ return interfaces::MakeChain(node); }, filter_type, cache_sizes.filter_index, false, do_reindex); InitBlockFilterIndex([&]{ return interfaces::MakeChain(node); }, filter_type, index_cache_sizes.filter_index, false, do_reindex);
node.indexes.emplace_back(GetBlockFilterIndex(filter_type)); node.indexes.emplace_back(GetBlockFilterIndex(filter_type));
} }

34
src/kernel/caches.h Normal file
View file

@ -0,0 +1,34 @@
// Copyright (c) 2024-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#ifndef BITCOIN_KERNEL_CACHES_H
#define BITCOIN_KERNEL_CACHES_H
#include <util/byte_units.h>
#include <algorithm>
//! Max memory allocated to block tree DB specific cache (bytes)
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};
namespace kernel {
struct CacheSizes {
size_t block_tree_db;
size_t coins_db;
size_t coins;
CacheSizes(size_t total_cache)
{
block_tree_db = std::min(total_cache / 8, MAX_BLOCK_DB_CACHE);
total_cache -= block_tree_db;
coins_db = std::min(total_cache / 2, MAX_COINS_DB_CACHE);
total_cache -= coins_db;
coins = total_cache; // the rest goes to the coins cache
}
};
} // namespace kernel
#endif // BITCOIN_KERNEL_CACHES_H

View file

@ -6,27 +6,25 @@
#include <common/args.h> #include <common/args.h>
#include <index/txindex.h> #include <index/txindex.h>
#include <kernel/caches.h>
#include <txdb.h> #include <txdb.h>
#include <algorithm>
#include <string>
namespace node { namespace node {
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes) CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
{ {
int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20); int64_t nTotalCache = (args.GetIntArg("-dbcache", nDefaultDbCache) << 20);
nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache nTotalCache = std::max(nTotalCache, nMinDbCache << 20); // total cache cannot be less than nMinDbCache
CacheSizes sizes; IndexCacheSizes sizes;
sizes.block_tree_db = std::min(nTotalCache / 8, nMaxBlockDBCache << 20);
nTotalCache -= sizes.block_tree_db;
sizes.tx_index = std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0); sizes.tx_index = std::min(nTotalCache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? nMaxTxIndexCache << 20 : 0);
nTotalCache -= sizes.tx_index; nTotalCache -= sizes.tx_index;
sizes.filter_index = 0;
if (n_indexes > 0) { if (n_indexes > 0) {
int64_t max_cache = std::min(nTotalCache / 8, max_filter_index_cache << 20); int64_t max_cache = std::min(nTotalCache / 8, max_filter_index_cache << 20);
sizes.filter_index = max_cache / n_indexes; sizes.filter_index = max_cache / n_indexes;
nTotalCache -= sizes.filter_index * n_indexes; nTotalCache -= sizes.filter_index * n_indexes;
} }
sizes.coins_db = std::min(nTotalCache / 2, nMaxCoinsDBCache << 20); return {sizes, kernel::CacheSizes{static_cast<size_t>(nTotalCache)}};
nTotalCache -= sizes.coins_db;
sizes.coins = nTotalCache; // the rest goes to in-memory cache
return sizes;
} }
} // namespace node } // namespace node

View file

@ -5,18 +5,21 @@
#ifndef BITCOIN_NODE_CACHES_H #ifndef BITCOIN_NODE_CACHES_H
#define BITCOIN_NODE_CACHES_H #define BITCOIN_NODE_CACHES_H
#include <kernel/caches.h>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
class ArgsManager; class ArgsManager;
namespace node { namespace node {
struct IndexCacheSizes {
int64_t tx_index{0};
int64_t filter_index{0};
};
struct CacheSizes { struct CacheSizes {
int64_t block_tree_db; IndexCacheSizes index;
int64_t coins_db; kernel::CacheSizes kernel;
int64_t coins;
int64_t tx_index;
int64_t filter_index;
}; };
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0); CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes = 0);
} // namespace node } // namespace node

View file

@ -8,9 +8,9 @@
#include <chain.h> #include <chain.h>
#include <coins.h> #include <coins.h>
#include <consensus/params.h> #include <consensus/params.h>
#include <kernel/caches.h>
#include <logging.h> #include <logging.h>
#include <node/blockstorage.h> #include <node/blockstorage.h>
#include <node/caches.h>
#include <sync.h> #include <sync.h>
#include <threadsafety.h> #include <threadsafety.h>
#include <tinyformat.h> #include <tinyformat.h>
@ -29,6 +29,8 @@
#include <memory> #include <memory>
#include <vector> #include <vector>
using kernel::CacheSizes;
namespace node { namespace node {
// Complete initialization of chainstates after the initial call has been made // Complete initialization of chainstates after the initial call has been made
// to ChainstateManager::InitializeChainstate(). // to ChainstateManager::InitializeChainstate().

View file

@ -14,9 +14,11 @@
class CTxMemPool; class CTxMemPool;
namespace node { namespace kernel {
struct CacheSizes; struct CacheSizes;
} // namespace kernel
namespace node {
struct ChainstateLoadOptions { struct ChainstateLoadOptions {
CTxMemPool* mempool{nullptr}; CTxMemPool* mempool{nullptr};
@ -69,7 +71,7 @@ using ChainstateLoadResult = std::tuple<ChainstateLoadStatus, bilingual_str>;
* *
* LoadChainstate returns a (status code, error string) tuple. * LoadChainstate returns a (status code, error string) tuple.
*/ */
ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const CacheSizes& cache_sizes, ChainstateLoadResult LoadChainstate(ChainstateManager& chainman, const kernel::CacheSizes& cache_sizes,
const ChainstateLoadOptions& options); const ChainstateLoadOptions& options);
ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options); ChainstateLoadResult VerifyLoadedChainstate(ChainstateManager& chainman, const ChainstateLoadOptions& options);
} // namespace node } // namespace node

View file

@ -66,7 +66,6 @@ using kernel::BlockTreeDB;
using node::ApplyArgsManOptions; using node::ApplyArgsManOptions;
using node::BlockAssembler; using node::BlockAssembler;
using node::BlockManager; using node::BlockManager;
using node::CalculateCacheSizes;
using node::KernelNotifications; using node::KernelNotifications;
using node::LoadChainstate; using node::LoadChainstate;
using node::RegenerateCommitments; using node::RegenerateCommitments;
@ -228,8 +227,6 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)
Assert(error.empty()); Assert(error.empty());
m_node.warnings = std::make_unique<node::Warnings>(); m_node.warnings = std::make_unique<node::Warnings>();
m_cache_sizes = CalculateCacheSizes(m_args);
m_node.notifications = std::make_unique<KernelNotifications>(Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings)); m_node.notifications = std::make_unique<KernelNotifications>(Assert(m_node.shutdown_request), m_node.exit_status, *Assert(m_node.warnings));
m_make_chainman = [this, &chainparams, opts] { m_make_chainman = [this, &chainparams, opts] {
@ -255,7 +252,7 @@ ChainTestingSetup::ChainTestingSetup(const ChainType chainType, TestOpts opts)
LOCK(m_node.chainman->GetMutex()); LOCK(m_node.chainman->GetMutex());
m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{ m_node.chainman->m_blockman.m_block_tree_db = std::make_unique<BlockTreeDB>(DBParams{
.path = m_args.GetDataDirNet() / "blocks" / "index", .path = m_args.GetDataDirNet() / "blocks" / "index",
.cache_bytes = static_cast<size_t>(m_cache_sizes.block_tree_db), .cache_bytes = static_cast<size_t>(m_kernel_cache_sizes.block_tree_db),
.memory_only = true, .memory_only = true,
}); });
}; };
@ -291,7 +288,7 @@ void ChainTestingSetup::LoadVerifyActivateChainstate()
options.check_blocks = m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS); options.check_blocks = m_args.GetIntArg("-checkblocks", DEFAULT_CHECKBLOCKS);
options.check_level = m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL); options.check_level = m_args.GetIntArg("-checklevel", DEFAULT_CHECKLEVEL);
options.require_full_verification = m_args.IsArgSet("-checkblocks") || m_args.IsArgSet("-checklevel"); options.require_full_verification = m_args.IsArgSet("-checkblocks") || m_args.IsArgSet("-checklevel");
auto [status, error] = LoadChainstate(chainman, m_cache_sizes, options); auto [status, error] = LoadChainstate(chainman, m_kernel_cache_sizes, options);
assert(status == node::ChainstateLoadStatus::SUCCESS); assert(status == node::ChainstateLoadStatus::SUCCESS);
std::tie(status, error) = VerifyLoadedChainstate(chainman, options); std::tie(status, error) = VerifyLoadedChainstate(chainman, options);

View file

@ -6,6 +6,7 @@
#define BITCOIN_TEST_UTIL_SETUP_COMMON_H #define BITCOIN_TEST_UTIL_SETUP_COMMON_H
#include <common/args.h> // IWYU pragma: export #include <common/args.h> // IWYU pragma: export
#include <kernel/caches.h>
#include <kernel/context.h> #include <kernel/context.h>
#include <key.h> #include <key.h>
#include <node/caches.h> #include <node/caches.h>
@ -103,7 +104,7 @@ struct BasicTestingSetup {
* initialization behaviour. * initialization behaviour.
*/ */
struct ChainTestingSetup : public BasicTestingSetup { struct ChainTestingSetup : public BasicTestingSetup {
node::CacheSizes m_cache_sizes{}; kernel::CacheSizes m_kernel_cache_sizes{node::CalculateCacheSizes(m_args).kernel};
bool m_coins_db_in_memory{true}; bool m_coins_db_in_memory{true};
bool m_block_tree_db_in_memory{true}; bool m_block_tree_db_in_memory{true};
std::function<void()> m_make_chainman{}; std::function<void()> m_make_chainman{};

View file

@ -27,16 +27,12 @@ static const int64_t nDefaultDbCache = 450;
static const int64_t nDefaultDbBatchSize = 16 << 20; static const int64_t nDefaultDbBatchSize = 16 << 20;
//! min. -dbcache (MiB) //! min. -dbcache (MiB)
static const int64_t nMinDbCache = 4; static const int64_t nMinDbCache = 4;
//! Max memory allocated to block tree DB specific cache (MiB)
static const int64_t nMaxBlockDBCache = 2;
// Unlike for the UTXO database, for the txindex scenario the leveldb cache make // Unlike for the UTXO database, for the txindex scenario the leveldb cache make
// a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991 // a meaningful difference: https://github.com/bitcoin/bitcoin/pull/8273#issuecomment-229601991
//! Max memory allocated to tx index DB specific cache in MiB. //! Max memory allocated to tx index DB specific cache in MiB.
static const int64_t nMaxTxIndexCache = 1024; static const int64_t nMaxTxIndexCache = 1024;
//! Max memory allocated to all block filter index caches combined in MiB. //! Max memory allocated to all block filter index caches combined in MiB.
static const int64_t max_filter_index_cache = 1024; static const int64_t max_filter_index_cache = 1024;
//! Max memory allocated to coin DB specific cache (MiB)
static const int64_t nMaxCoinsDBCache = 8;
//! User-controlled performance and debug options. //! User-controlled performance and debug options.
struct CoinsViewOptions { struct CoinsViewOptions {