bitcoin/src/bench/checkblock.cpp
Lőrinc 073e28b1e9 optimization: add single byte writes
Single byte writes are used very often (used for every (u)int8_t or std::byte or bool and for every VarInt's first byte which is also needed for every (pre)Vector).
It makes sense to avoid the generalized serialization infrastructure that isn't needed:
* AutoFile write doesn't need to allocate 4k buffer for a single byte now;
* `VectorWriter` and `DataStream` avoids memcpy/insert calls.

> cmake -B build -DBUILD_BENCH=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build -j$(nproc) && build/bin/bench_bitcoin -filter='SizeComputerBlock|SerializeBlock' --min-time=10000

> C compiler ............................ AppleClang 16.0.0.16000026

|            ns/block |             block/s |    err% |     total | benchmark
|--------------------:|--------------------:|--------:|----------:|:----------
|          174,569.19 |            5,728.39 |    0.6% |     10.89 | `SerializeBlock`
|           10,241.16 |           97,645.21 |    0.0% |     11.00 | `SizeComputerBlock`

> C++ compiler .......................... GNU 13.3.0

|            ns/block |             block/s |    err% |       ins/block |       cyc/block |    IPC |      bra/block |   miss% |     total | benchmark
|--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:----------
|          615,000.56 |            1,626.01 |    0.0% |    8,015,883.64 |    2,208,340.88 |  3.630 |   1,517,035.62 |    0.5% |     10.56 | `SerializeBlock`
|           25,676.76 |           38,945.72 |    0.0% |      159,390.03 |       92,202.10 |  1.729 |      42,131.03 |    0.9% |     11.00 | `SizeComputerBlock`
2025-04-19 20:53:33 +02:00

89 lines
2.9 KiB
C++

// Copyright (c) 2016-2022 The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bench/bench.h>
#include <bench/data/block413567.raw.h>
#include <chainparams.h>
#include <common/args.h>
#include <consensus/validation.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <serialize.h>
#include <span.h>
#include <streams.h>
#include <util/chaintype.h>
#include <validation.h>
#include <cassert>
#include <cstddef>
#include <memory>
#include <optional>
#include <vector>
static void SizeComputerBlock(benchmark::Bench& bench) {
CBlock block;
DataStream(benchmark::data::block413567) >> TX_WITH_WITNESS(block);
bench.unit("block").run([&] {
SizeComputer size_computer;
size_computer << TX_WITH_WITNESS(block);
assert(size_computer.size() == benchmark::data::block413567.size());
});
}
static void SerializeBlock(benchmark::Bench& bench) {
CBlock block;
DataStream(benchmark::data::block413567) >> TX_WITH_WITNESS(block);
// Create output stream and verify first serialization matches input
bench.unit("block").run([&] {
DataStream output_stream(benchmark::data::block413567.size());
output_stream << TX_WITH_WITNESS(block);
assert(output_stream.size() == benchmark::data::block413567.size());
});
}
// These are the two major time-sinks which happen after we have fully received
// a block off the wire, but before we can relay the block on to peers using
// compact block relay.
static void DeserializeBlock(benchmark::Bench& bench)
{
DataStream stream(benchmark::data::block413567);
std::byte a{0};
stream.write(std::span{&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);
});
}
static void DeserializeAndCheckBlock(benchmark::Bench& bench)
{
DataStream stream(benchmark::data::block413567);
std::byte a{0};
stream.write(std::span{&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);
});
}
BENCHMARK(SizeComputerBlock, benchmark::PriorityLevel::HIGH);
BENCHMARK(SerializeBlock, benchmark::PriorityLevel::HIGH);
BENCHMARK(DeserializeBlock, benchmark::PriorityLevel::HIGH);
BENCHMARK(DeserializeAndCheckBlock, benchmark::PriorityLevel::HIGH);