Merge bitcoin/bitcoin#35124: bench: fix benchmark fixtures and setup checks

e6430b2773 bench: make `setup()` use single-iteration epochs (Lőrinc)
ba0078e3bf bench: fix ephemeral spend inputs (Lőrinc)
b8b7f896e8 bench: drop duplicate balance benchmark (Lőrinc)

Pull request description:

  ### Context
  Found while reviewing https://github.com/bitcoin/bitcoin/pull/35018, which led to a few additional benchmark fixes.

  ### Problem
  `MempoolCheckEphemeralSpends` populated every child input by writing to `vin[0]`.
  That left only one child input pointing at the parent transaction, while the remaining inputs kept default prevouts.

  `WalletBalanceMine` duplicated `WalletBalanceClean` exactly.

  The same review also showed that nanobench `setup()` can be misused as though it runs once per timed call.
  It actually runs once per epoch, so using it with multiple epoch iterations can silently leave later iterations with different preconditions.

  ### Fix
  Write each ephemeral-spend prevout to the matching child input and assert that the last input spends the last parent output.
  Remove the duplicate wallet balance benchmark registration.
  Add a nanobench assertion that `setup()` is only used with `epochIterations(1)`.

  > [!NOTE]
  > This PR included a few benchmark adjustments originally which made the tests unstable and were reverted.

ACKs for top commit:
  davidgumberg:
    crACK e6430b2773
  sedited:
    ACK e6430b2773

Tree-SHA512: 667c67975d7a3e21333f6c8a4e037ad700bc6dcbd7039e002534e4f2a9328195a618b78accf097e7c43402a1f1b25e32b0c39642fd465729ab4c337abe4f23f1
This commit is contained in:
merge-script
2026-04-23 22:45:07 -04:00
9 changed files with 14 additions and 15 deletions

View File

@@ -162,8 +162,7 @@ static void AddrManAddThenGood(benchmark::Bench& bench)
CreateAddresses();
std::optional<AddrMan> addrman;
bench.epochIterations(1)
.setup([&] {
bench.setup([&] {
addrman.emplace(EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO);
AddAddressesToAddrMan(*addrman);
})

View File

@@ -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);

View File

@@ -115,8 +115,7 @@ static void BnBExhaustion(benchmark::Bench& bench)
{
std::vector<OutputGroup> 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);

View File

@@ -62,8 +62,7 @@ static void LoadExternalBlockFile(benchmark::Bench& bench)
std::multimap<uint256, FlatFilePos> blocks_with_unknown_parent;
FlatFilePos pos;
bench.epochIterations(1)
.setup([&] {
bench.setup([&] {
blocks_with_unknown_parent.clear();
pos = FlatFilePos{};
})

View File

@@ -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};

View File

@@ -40,6 +40,7 @@
///////////////////////////////////////////////////////////////////////////////////////////////////
#include <chrono> // high_resolution_clock
#include <cassert> // assert
#include <cstring> // memcpy
#include <iosfwd> // for std::ostream* custom output target in Config
#include <string> // 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 <typename Op>
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>(op));
}

View File

@@ -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);

View File

@@ -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;

View File

@@ -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