From 8825051e0845e036ca284717806468193d4501b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sat, 3 Jan 2026 15:39:13 +0100 Subject: [PATCH] refactor: improve benchmark setup and execution for various tests Note that `make_hard_case` already clears the UTXO pool in `coin_selection.cpp`. ./build/bin/bench_bitcoin -filter='^(BnBExhaustion|AddrManAddThenGood|DeserializeBlockTest|DeserializeAndCheckBlockTest|CheckBlockTest|LoadExternalBlockFile|FindByte|WalletCreatePlain|WalletCreateEncrypted|WalletLoadingDescriptors)$' | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 15,088,500.00 | 66.28 | 0.2% | 0.17 | `AddrManAddThenGood` | 179,208.00 | 5,580.11 | 2.0% | 0.00 | `BnBExhaustion` | ns/block | block/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 318,166.00 | 3,143.01 | 3.5% | 0.00 | `CheckBlockTest` | 886,750.00 | 1,127.71 | 0.8% | 0.01 | `DeserializeBlockTest` | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 42.00 | 23,809,523.81 | 2.4% | 0.00 | `FindByte` | 5,473,208.00 | 182.71 | 0.4% | 0.06 | `LoadExternalBlockFile` | 584,168,041.00 | 1.71 | 0.3% | 6.43 | `WalletCreateEncrypted` | 168,040,458.00 | 5.95 | 1.1% | 1.85 | `WalletCreatePlain` | 155,446,625.00 | 6.43 | 0.7% | 0.78 | `WalletLoadingDescriptors` | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 14,894,917.00 | 67.14 | 0.3% | 0.16 | `AddrManAddThenGood` | 177,667.00 | 5,628.51 | 1.3% | 0.00 | `BnBExhaustion` | ns/block | block/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 313,791.00 | 3,186.83 | 3.8% | 0.00 | `CheckBlockTest` | 888,208.00 | 1,125.86 | 0.7% | 0.01 | `DeserializeBlockTest` | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 41.00 | 24,390,243.90 | 2.4% | 0.00 | `FindByte` | 5,445,208.00 | 183.65 | 1.0% | 0.06 | `LoadExternalBlockFile` | 581,800,500.00 | 1.72 | 0.4% | 6.40 | `WalletCreateEncrypted` | 166,035,583.00 | 6.02 | 0.5% | 1.82 | `WalletCreatePlain` | 153,574,792.00 | 6.51 | 0.1% | 0.77 | `WalletLoadingDescriptors` --- src/bench/addrman.cpp | 19 ++++++--------- src/bench/checkblock.cpp | 44 ++++++++++++++-------------------- src/bench/coin_selection.cpp | 17 ++++++------- src/bench/load_external.cpp | 17 ++++++++----- src/bench/streams_findbyte.cpp | 7 +++--- 5 files changed, 46 insertions(+), 58 deletions(-) diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp index d28030e4baa..fc081d9ff4b 100644 --- a/src/bench/addrman.cpp +++ b/src/bench/addrman.cpp @@ -161,18 +161,13 @@ static void AddrManAddThenGood(benchmark::Bench& bench) CreateAddresses(); - bench.run([&] { - // To make the benchmark independent of the number of evaluations, we always prepare a new addrman. - // This is necessary because AddrMan::Good() method modifies the object, affecting the timing of subsequent calls - // to the same method and we want to do the same amount of work in every loop iteration. - // - // This has some overhead (exactly the result of AddrManAdd benchmark), but that overhead is constant so improvements in - // AddrMan::Good() will still be noticeable. - AddrMan addrman{EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO}; - AddAddressesToAddrMan(addrman); - - markSomeAsGood(addrman); - }); + std::optional addrman; + bench.epochIterations(1) + .setup([&] { + addrman.emplace(EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO); + AddAddressesToAddrMan(*addrman); + }) + .run([&] { markSomeAsGood(*addrman); }); } BENCHMARK(AddrManAdd); diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index 765b8b0dadc..cbba543f2d8 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -27,38 +27,30 @@ static void DeserializeBlockTest(benchmark::Bench& bench) { - DataStream stream(benchmark::data::block413567); - std::byte a{0}; - stream.write({&a, 1}); // Prevent compaction - - bench.unit("block").run([&] { - CBlock block; - stream >> TX_WITH_WITNESS(block); - bool rewound = stream.Rewind(benchmark::data::block413567.size()); - assert(rewound); - }); + DataStream stream; + bench.unit("block").epochIterations(1) + .setup([&] { stream = DataStream{benchmark::data::block413567}; }) + .run([&] { CBlock block; stream >> TX_WITH_WITNESS(block); }); } -static void DeserializeAndCheckBlockTest(benchmark::Bench& bench) +static void CheckBlockTest(benchmark::Bench& bench) { - DataStream stream(benchmark::data::block413567); - std::byte a{0}; - stream.write({&a, 1}); // Prevent compaction - ArgsManager bench_args; const auto chainParams = CreateChainParams(bench_args, ChainType::MAIN); - bench.unit("block").run([&] { - CBlock block; // Note that CBlock caches its checked state, so we need to recreate it here - stream >> TX_WITH_WITNESS(block); - bool rewound = stream.Rewind(benchmark::data::block413567.size()); - assert(rewound); - - BlockValidationState validationState; - bool checked = CheckBlock(block, validationState, chainParams->GetConsensus()); - assert(checked); - }); + CBlock block; + bench.unit("block").epochIterations(1) + .setup([&] { + block = CBlock{}; + DataStream stream{benchmark::data::block413567}; + stream >> TX_WITH_WITNESS(block); + }) + .run([&] { + BlockValidationState validationState; + bool checked = CheckBlock(block, validationState, chainParams->GetConsensus()); + assert(checked); + }); } BENCHMARK(DeserializeBlockTest); -BENCHMARK(DeserializeAndCheckBlockTest); +BENCHMARK(CheckBlockTest); diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 72b3d70ec68..801aee2cc08 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -124,17 +124,14 @@ static CAmount make_hard_case(int utxos, std::vector& utxo_pool) static void BnBExhaustion(benchmark::Bench& bench) { - // Setup std::vector utxo_pool; - - bench.run([&] { - // Benchmark - CAmount target = make_hard_case(17, utxo_pool); - (void)SelectCoinsBnB(utxo_pool, target, /*cost_of_change=*/0, MAX_STANDARD_TX_WEIGHT); // Should exhaust - - // Cleanup - utxo_pool.clear(); - }); + CAmount target; + bench.epochIterations(1) + .setup([&] { target = make_hard_case(17, utxo_pool); }) + .run([&] { + auto res{SelectCoinsBnB(utxo_pool, target, /*cost_of_change=*/0, MAX_STANDARD_TX_WEIGHT)}; // Should exhaust + ankerl::nanobench::doNotOptimizeAway(res); + }); } BENCHMARK(CoinSelection); diff --git a/src/bench/load_external.cpp b/src/bench/load_external.cpp index 3350d16ac02..128d531f97c 100644 --- a/src/bench/load_external.cpp +++ b/src/bench/load_external.cpp @@ -62,12 +62,17 @@ static void LoadExternalBlockFile(benchmark::Bench& bench) std::multimap blocks_with_unknown_parent; FlatFilePos pos; - bench.run([&] { - // "rb" is "binary, O_RDONLY", positioned to the start of the file. - // The file will be closed by LoadExternalBlockFile(). - AutoFile file{fsbridge::fopen(blkfile, "rb")}; - testing_setup->m_node.chainman->LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent); - }); + bench.epochIterations(1) + .setup([&] { + blocks_with_unknown_parent.clear(); + pos = FlatFilePos{}; + }) + .run([&] { + // "rb" is "binary, O_RDONLY", positioned to the start of the file. + // The file will be closed by LoadExternalBlockFile(). + AutoFile file{fsbridge::fopen(blkfile, "rb")}; + testing_setup->m_node.chainman->LoadExternalBlockFile(file, &pos, &blocks_with_unknown_parent); + }); fs::remove(blkfile); } diff --git a/src/bench/streams_findbyte.cpp b/src/bench/streams_findbyte.cpp index 45e93d777d8..47b2ad740ca 100644 --- a/src/bench/streams_findbyte.cpp +++ b/src/bench/streams_findbyte.cpp @@ -22,10 +22,9 @@ static void FindByte(benchmark::Bench& bench) file.seek(0, SEEK_SET); BufferedFile bf{file, /*nBufSize=*/file_size + 1, /*nRewindIn=*/file_size}; - bench.run([&] { - bf.SetPos(0); - bf.FindByte(std::byte(1)); - }); + bench.epochIterations(1) + .setup([&] { bf.SetPos(0); }) + .run([&] { bf.FindByte(std::byte(1)); }); assert(file.fclose() == 0); }