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`
This commit is contained in:
Lőrinc
2026-01-03 15:39:13 +01:00
parent 83b8528ddb
commit 8825051e08
5 changed files with 46 additions and 58 deletions

View File

@@ -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> addrman;
bench.epochIterations(1)
.setup([&] {
addrman.emplace(EMPTY_NETGROUPMAN, /*deterministic=*/false, ADDRMAN_CONSISTENCY_CHECK_RATIO);
AddAddressesToAddrMan(*addrman);
})
.run([&] { markSomeAsGood(*addrman); });
}
BENCHMARK(AddrManAdd);

View File

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

View File

@@ -124,17 +124,14 @@ static CAmount make_hard_case(int utxos, std::vector<OutputGroup>& utxo_pool)
static void BnBExhaustion(benchmark::Bench& bench)
{
// Setup
std::vector<OutputGroup> 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);

View File

@@ -62,12 +62,17 @@ static void LoadExternalBlockFile(benchmark::Bench& bench)
std::multimap<uint256, FlatFilePos> 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);
}

View File

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