Skip to content

Commit

Permalink
assumeutxo benchmarking patch
Browse files Browse the repository at this point in the history
introduce two commandline options for assumeutxo, specifically for
benchmarking. these commands are:

- pausebackgroundsync - an option lets the user pause the verification
                        of historical blocks in the background
-loadutxosnapshot=<path> - load an assumeutxo snapshot on startup,
                           instead of needing to go through the rpc
                           command. the node will shutdown immediately
                           after the snapshot has been loaded

this path is not meant for general use and is instead just for making it
more ergonomic to use assumeutxo for benchmarking IBD changes. the
benefits of using assumeutxo here are we can start from an arbitrary
height and sync to chaintip to collect relevant data quickly. using
assumeutxo means we can make whatever changes we need to the
chainstatedb, since it will be created fresh from the snapshot.

note, to use the loadutxosnapshot option, you must first run:

    ./build/src/bitcoind -stopatheight=1

this makes the node do a header sync and then shut down. this is because
assumeutxo will not load a snapshot unless the base block is in the
header chain. we could remove this requirement, but this patch is meant
to be as minimal as possible, and this also allows us to perform heaeder
sync as a preparation commit for a benchmark, which helps keep IBD
benchmarks more focused on strictly measuring IBD.

next, run:

    ./build/src/bitcoind -loadutxosnapshot=<path>

the node will shutdown after the snapshot is loaded. finally, run:

    ./build/src/bitcoind -pausebackgroundsync=1

for the actual benchmarking step. this ensures only the sync to chaintip
is benchmarked and the load snapshot step is not included in the
measurement.

Co-authored-by: Sjors Provoost <[email protected]>
  • Loading branch information
josibake and Sjors committed Nov 5, 2024
1 parent fcb01ea commit 963472a
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 4 deletions.
59 changes: 58 additions & 1 deletion src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <bitcoin-build-config.h> // IWYU pragma: keep
#include "node/utxo_snapshot.h"

#include <init.h>

Expand Down Expand Up @@ -140,6 +141,7 @@ using node::VerifyLoadedChainstate;
using util::Join;
using util::ReplaceAll;
using util::ToString;
using node::SnapshotMetadata;

static constexpr bool DEFAULT_PROXYRANDOMIZE{true};
static constexpr bool DEFAULT_REST_ENABLE{false};
Expand All @@ -158,6 +160,44 @@ static constexpr bool DEFAULT_STOPAFTERBLOCKIMPORT{false};
static constexpr int MIN_CORE_FDS = MIN_LEVELDB_FDS + NUM_FDS_MESSAGE_CAPTURE;
static const char* DEFAULT_ASMAP_FILENAME="ip_asn.map";

bool LoadUTXOSnapshot(NodeContext& node, const fs::path& snapshot_path) {
ChainstateManager& chainman = *node.chainman;

FILE* file{fsbridge::fopen(snapshot_path, "rb")};
AutoFile afile{file};
if (afile.IsNull()) {
LogPrintf("Error: Couldn't open UTXO snapshot file %s for reading\n", snapshot_path.utf8string());
return false;
}

SnapshotMetadata metadata{chainman.GetParams().MessageStart()};
try {
afile >> metadata;
} catch (const std::ios_base::failure& e) {
LogPrintf("Error: Unable to parse snapshot metadata: %s\n", e.what());
return false;
}

auto activation_result{chainman.ActivateSnapshot(afile, metadata, false)};
if (!activation_result) {
LogPrintf("Error: Unable to load UTXO snapshot: %s\n",
util::ErrorString(activation_result).original);
return false;
}

// Update services to reflect limited peer capabilities during sync
node.connman->RemoveLocalServices(NODE_NETWORK);
node.connman->AddLocalServices(NODE_NETWORK_LIMITED);

CBlockIndex& snapshot_index{*CHECK_NONFATAL(*activation_result)};
LogPrintf("Loaded UTXO snapshot: coins=%d, height=%d, hash=%s\n",
metadata.m_coins_count,
snapshot_index.nHeight,
snapshot_index.GetBlockHash().ToString());

return true;
}

/**
* The PID file facilities.
*/
Expand Down Expand Up @@ -495,6 +535,12 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
argsman.AddArg("-minimumchainwork=<hex>", strprintf("Minimum work assumed to exist on a valid chain in hex (default: %s, testnet3: %s, testnet4: %s, signet: %s)", defaultChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnetChainParams->GetConsensus().nMinimumChainWork.GetHex(), testnet4ChainParams->GetConsensus().nMinimumChainWork.GetHex(), signetChainParams->GetConsensus().nMinimumChainWork.GetHex()), ArgsManager::ALLOW_ANY | ArgsManager::DEBUG_ONLY, OptionsCategory::OPTIONS);
argsman.AddArg("-par=<n>", strprintf("Set the number of script verification threads (0 = auto, up to %d, <0 = leave that many cores free, default: %d)",
MAX_SCRIPTCHECK_THREADS, DEFAULT_SCRIPTCHECK_THREADS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-pausebackgroundsync", strprintf("When a UTXO snapshot is loaded, pause the verification of historical blocks in the background (default: %u)", DEFAULT_PAUSE_BACKGROUND_SYNC), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
gArgs.AddArg("-loadutxosnapshot=<path>",
"Load UTXO set from snapshot file at startup. "
"This allows fast synchronization by loading a pre-built UTXO "
"snapshot while the full chain validation happens in background.",
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempool", strprintf("Whether to save the mempool on shutdown and load on restart (default: %u)", DEFAULT_PERSIST_MEMPOOL), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
argsman.AddArg("-persistmempoolv1",
strprintf("Whether a mempool.dat file created by -persistmempool or the savemempool RPC will be written in the legacy format "
Expand Down Expand Up @@ -1661,6 +1707,15 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)

ChainstateManager& chainman = *Assert(node.chainman);

if (args.IsArgSet("-loadutxosnapshot")) {
fs::path snapshot_path = fs::u8path(args.GetArg("-loadutxosnapshot", ""));
snapshot_path = AbsPathForConfigVal(args, snapshot_path);

if (!LoadUTXOSnapshot(node, snapshot_path)) {
LogPrintf("Failed to load UTXO snapshot from %s", snapshot_path.utf8string());
}
}

assert(!node.peerman);
node.peerman = PeerManager::make(*node.connman, *node.addrman,
node.banman.get(), chainman,
Expand Down Expand Up @@ -1800,7 +1855,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
});
}

if (ShutdownRequested(node)) {
// if loadutxosnapshot is set, we want to load the snapshot then shut down so that only
// syncing to chaintip is benchmarked
if (ShutdownRequested(node) || args.IsArgSet("-loadutxosnapshot")) {
return false;
}

Expand Down
2 changes: 2 additions & 0 deletions src/kernel/chainstatemanager_opts.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ struct ChainstateManagerOpts {
int worker_threads_num{0};
size_t script_execution_cache_bytes{DEFAULT_SCRIPT_EXECUTION_CACHE_BYTES};
size_t signature_cache_bytes{DEFAULT_SIGNATURE_CACHE_BYTES};
//! Whether to defer syncing the background chainstate after an assumeutxo snapshot is loaded
bool pause_background_sync{false};
};

} // namespace kernel
Expand Down
2 changes: 2 additions & 0 deletions src/node/chainstatemanager_args.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManage
opts.signature_cache_bytes = clamped_size_each;
}

opts.pause_background_sync = args.GetBoolArg("-pausebackgroundsync", DEFAULT_PAUSE_BACKGROUND_SYNC);

return {};
}
} // namespace node
2 changes: 2 additions & 0 deletions src/node/chainstatemanager_args.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ class ArgsManager;
static constexpr int MAX_SCRIPTCHECK_THREADS{15};
/** -par default (number of script-checking threads, 0 = auto) */
static constexpr int DEFAULT_SCRIPTCHECK_THREADS{0};
/** -pausebackgroundsync default */
static const bool DEFAULT_PAUSE_BACKGROUND_SYNC{false};

namespace node {
[[nodiscard]] util::Result<void> ApplyArgsManOptions(const ArgsManager& args, ChainstateManager::Options& opts);
Expand Down
6 changes: 6 additions & 0 deletions src/validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6394,6 +6394,12 @@ std::optional<int> ChainstateManager::GetSnapshotBaseHeight() const
return base ? std::make_optional(base->nHeight) : std::nullopt;
}

bool ChainstateManager::BackgroundSyncInProgress() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) {
if (!IsUsable(m_snapshot_chainstate.get())) return false;
if (!IsUsable(m_ibd_chainstate.get())) return false;
return !m_options.pause_background_sync;
}

bool ChainstateManager::ValidatedSnapshotCleanup()
{
AssertLockHeld(::cs_main);
Expand Down
4 changes: 1 addition & 3 deletions src/validation.h
Original file line number Diff line number Diff line change
Expand Up @@ -1113,9 +1113,7 @@ class ChainstateManager
CBlockIndex* ActiveTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) { return ActiveChain().Tip(); }

//! The state of a background sync (for net processing)
bool BackgroundSyncInProgress() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) {
return IsUsable(m_snapshot_chainstate.get()) && IsUsable(m_ibd_chainstate.get());
}
bool BackgroundSyncInProgress() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex());

//! The tip of the background sync chain
const CBlockIndex* GetBackgroundSyncTip() const EXCLUSIVE_LOCKS_REQUIRED(GetMutex()) {
Expand Down

0 comments on commit 963472a

Please sign in to comment.