From b8b7f896e8f87bd9a815525f71236a69dc8ae550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Thu, 23 Apr 2026 20:27:42 +0200 Subject: [PATCH 1/3] bench: drop duplicate balance benchmark `WalletBalanceMine` duplicated `WalletBalanceClean` exactly. Remove the duplicate registration so the balance benchmark list stays distinct. --- src/bench/wallet_balance.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/bench/wallet_balance.cpp b/src/bench/wallet_balance.cpp index 45f23494280..d9cd4bbb952 100644 --- a/src/bench/wallet_balance.cpp +++ b/src/bench/wallet_balance.cpp @@ -62,11 +62,9 @@ static void WalletBalance(benchmark::Bench& bench, const bool set_dirty, const b static void WalletBalanceDirty(benchmark::Bench& bench) { WalletBalance(bench, /*set_dirty=*/true, /*add_mine=*/true); } static void WalletBalanceClean(benchmark::Bench& bench) { WalletBalance(bench, /*set_dirty=*/false, /*add_mine=*/true); } -static void WalletBalanceMine(benchmark::Bench& bench) { WalletBalance(bench, /*set_dirty=*/false, /*add_mine=*/true); } static void WalletBalanceWatch(benchmark::Bench& bench) { WalletBalance(bench, /*set_dirty=*/false, /*add_mine=*/false); } BENCHMARK(WalletBalanceDirty); BENCHMARK(WalletBalanceClean); -BENCHMARK(WalletBalanceMine); BENCHMARK(WalletBalanceWatch); } // namespace wallet From ba0078e3bf10101329f8cfafe8e34e0e397a32d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Thu, 23 Apr 2026 20:27:48 +0200 Subject: [PATCH 2/3] bench: fix ephemeral spend inputs `MempoolCheckEphemeralSpends` wrote every prevout to `tx2.vin[0]` instead of `tx2.vin[i]`. That left only one child input pointing at the parent transaction, while the remaining inputs kept default prevouts. Write each prevout to `vin[i]` instead. Add an assertion that the last child input spends the last parent output. Co-authored-by: David Gumberg --- src/bench/mempool_ephemeral_spends.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/bench/mempool_ephemeral_spends.cpp b/src/bench/mempool_ephemeral_spends.cpp index f0d8eb0bea7..1c0692878bd 100644 --- a/src/bench/mempool_ephemeral_spends.cpp +++ b/src/bench/mempool_ephemeral_spends.cpp @@ -59,8 +59,8 @@ static void MempoolCheckEphemeralSpends(benchmark::Bench& bench) CMutableTransaction tx2; tx2.vin.resize(tx1.vout.size()); for (size_t i = 0; i < tx2.vin.size(); i++) { - tx2.vin[0].prevout.hash = parent_txid; - tx2.vin[0].prevout.n = i; + tx2.vin[i].prevout.hash = parent_txid; + tx2.vin[i].prevout.n = i; } tx2.vout.resize(1); @@ -71,6 +71,7 @@ static void MempoolCheckEphemeralSpends(benchmark::Bench& bench) const CTransactionRef tx2_r{MakeTransactionRef(tx2)}; AddTx(tx1_r, pool); + assert(tx2_r->vin.back().prevout == COutPoint(parent_txid, tx1_r->vout.size() - 1)); uint32_t iteration{0}; From e6430b2773436c65d447fff5e0a326087abc4950 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Thu, 23 Apr 2026 20:27:56 +0200 Subject: [PATCH 3/3] bench: make `setup()` use single-iteration epochs `setup()` in nanobench runs once per epoch, not once per timed call. If an epoch executes the benchmark body multiple times, `setup()` can silently leave later iterations with different preconditions. Make `setup()` force `epochIterations(1)` itself: keep rejecting incompatible larger explicit epoch sizes, but allow existing single-iteration callers such as `-sanity-check`. With `setup()` handling this centrally, remove the redundant `epochIterations(1)` calls from the benchmarks that use it. Co-authored-by: David Gumberg --- src/bench/addrman.cpp | 3 +-- src/bench/checkblock.cpp | 2 +- src/bench/coin_selection.cpp | 3 +-- src/bench/load_external.cpp | 3 +-- src/bench/nanobench.h | 6 +++++- src/bench/streams_findbyte.cpp | 3 +-- src/bench/verify_script.cpp | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/bench/addrman.cpp b/src/bench/addrman.cpp index fc081d9ff4b..703b4d245db 100644 --- a/src/bench/addrman.cpp +++ b/src/bench/addrman.cpp @@ -162,8 +162,7 @@ static void AddrManAddThenGood(benchmark::Bench& bench) CreateAddresses(); std::optional addrman; - bench.epochIterations(1) - .setup([&] { + bench.setup([&] { addrman.emplace(EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO); AddAddressesToAddrMan(*addrman); }) diff --git a/src/bench/checkblock.cpp b/src/bench/checkblock.cpp index 9faf9ac137a..b943dc0f82b 100644 --- a/src/bench/checkblock.cpp +++ b/src/bench/checkblock.cpp @@ -33,7 +33,7 @@ static void CheckBlockTest(benchmark::Bench& bench) const auto block_data{benchmark::data::block413567}; CBlock block; - bench.unit("block").epochIterations(1) + bench.unit("block") .setup([&] { block = CBlock{}; SpanReader{block_data} >> TX_WITH_WITNESS(block); diff --git a/src/bench/coin_selection.cpp b/src/bench/coin_selection.cpp index 7a7f6dafbd2..9f0bf2cc104 100644 --- a/src/bench/coin_selection.cpp +++ b/src/bench/coin_selection.cpp @@ -115,8 +115,7 @@ static void BnBExhaustion(benchmark::Bench& bench) { std::vector utxo_pool; CAmount target; - bench.epochIterations(1) - .setup([&] { target = make_hard_case(17, utxo_pool); }) + bench.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); diff --git a/src/bench/load_external.cpp b/src/bench/load_external.cpp index 128d531f97c..8e7201eaa52 100644 --- a/src/bench/load_external.cpp +++ b/src/bench/load_external.cpp @@ -62,8 +62,7 @@ static void LoadExternalBlockFile(benchmark::Bench& bench) std::multimap blocks_with_unknown_parent; FlatFilePos pos; - bench.epochIterations(1) - .setup([&] { + bench.setup([&] { blocks_with_unknown_parent.clear(); pos = FlatFilePos{}; }) diff --git a/src/bench/nanobench.h b/src/bench/nanobench.h index 1f798b848b3..79a384aa56f 100644 --- a/src/bench/nanobench.h +++ b/src/bench/nanobench.h @@ -40,6 +40,7 @@ /////////////////////////////////////////////////////////////////////////////////////////////////// #include // high_resolution_clock +#include // assert #include // memcpy #include // for std::ostream* custom output target in Config #include // all names @@ -1013,7 +1014,7 @@ public: ANKERL_NANOBENCH(NODISCARD) Config const& config() const noexcept; /** - * @brief Configure an untimed setup step per epoch (fluent API). + * @brief Configure an untimed setup step per epoch (forces single-iteration epochs). * * Example: `bench.setup(...).run(...);` */ @@ -1238,6 +1239,9 @@ public: template ANKERL_NANOBENCH_NO_SANITIZE("integer") Bench& run(Op&& op) { + assert((mBench.epochIterations() <= 1) && + "setup() runs once per epoch, not once per iteration; it requires epochIterations(1)"); + mBench.epochIterations(1); return mBench.runImpl(mSetupOp, std::forward(op)); } diff --git a/src/bench/streams_findbyte.cpp b/src/bench/streams_findbyte.cpp index 47b2ad740ca..c9ea486c697 100644 --- a/src/bench/streams_findbyte.cpp +++ b/src/bench/streams_findbyte.cpp @@ -22,8 +22,7 @@ static void FindByte(benchmark::Bench& bench) file.seek(0, SEEK_SET); BufferedFile bf{file, /*nBufSize=*/file_size + 1, /*nRewindIn=*/file_size}; - bench.epochIterations(1) - .setup([&] { bf.SetPos(0); }) + bench.setup([&] { bf.SetPos(0); }) .run([&] { bf.FindByte(std::byte(1)); }); assert(file.fclose() == 0); diff --git a/src/bench/verify_script.cpp b/src/bench/verify_script.cpp index af254ae7783..0a92a057947 100644 --- a/src/bench/verify_script.cpp +++ b/src/bench/verify_script.cpp @@ -115,7 +115,7 @@ static void VerifyNestedIfScript(benchmark::Bench& bench) for (int i = 0; i < 100; ++i) { script << OP_ENDIF; } - bench.unit("script").epochIterations(1) + bench.unit("script") .setup([&] { stack.clear(); }) .run([&] { ScriptError error;