From 23ed684c6a6f93b6645ad7456d7439c6a32db687 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C5=91rinc?= Date: Sun, 26 Jan 2025 17:22:47 +0100 Subject: [PATCH] optimization: Bulk serialization writes in `SaveBlockUndo` and `SaveBlock` Similarly to the serialization reads, buffered writes will enable batched xor calculations - especially since currently we need to copy the write inputs Span to do the obfuscation on it, batching enables doing the xor on the internal buffer instead. > cmake -B build -DBUILD_BENCH=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build -j$(nproc) && build/src/bench/bench_bitcoin -filter='SaveBlockBench' -min-time=10000 > C++ compiler .......................... AppleClang 16.0.0.16000026 Before: | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 5,267,613.94 | 189.84 | 1.0% | 11.05 | `SaveBlockBench` After: | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 1,767,367.40 | 565.81 | 1.6% | 10.86 | `SaveBlockBench` > C++ compiler .......................... GNU 13.3.0 Before: | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- | 4,128,530.90 | 242.22 | 3.8% | 19,358,001.33 | 8,601,983.31 | 2.250 | 3,079,334.76 | 0.4% | 10.64 | `SaveBlockBench` After: | ns/op | op/s | err% | ins/op | cyc/op | IPC | bra/op | miss% | total | benchmark |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- | 3,130,556.05 | 319.43 | 4.7% | 17,305,378.56 | 6,457,946.37 | 2.680 | 2,579,854.87 | 0.3% | 10.83 | `SaveBlockBench` Co-authored-by: Cory Fields --- src/node/blockstorage.cpp | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/node/blockstorage.cpp b/src/node/blockstorage.cpp index de4f893e513..9cdb0b22700 100644 --- a/src/node/blockstorage.cpp +++ b/src/node/blockstorage.cpp @@ -974,16 +974,16 @@ bool BlockManager::WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationSt } // Write index header - fileout << GetParams().MessageStart() << blockundo_size; - // Write undo data + fileout.write_large(DataStream(BLOCK_SERIALIZATION_HEADER_SIZE) << GetParams().MessageStart() << blockundo_size); pos.nPos += BLOCK_SERIALIZATION_HEADER_SIZE; - fileout << blockundo; + { + // Calculate checksum + HashWriter hasher{}; + hasher << block.pprev->GetBlockHash() << blockundo; - // Calculate & write checksum - HashWriter hasher{}; - hasher << block.pprev->GetBlockHash(); - hasher << blockundo; - fileout << hasher.GetHash(); + // Write undo data & checksum + fileout.write_large(DataStream(blockundo_size + sizeof(uint256)) << blockundo << hasher.GetHash()); + } // rev files are written in block height order, whereas blk files are written as blocks come in (often out of order) // we want to flush the rev (undo) file once we've written the last block, which is indicated by the last height @@ -1135,10 +1135,11 @@ FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight) } // Write index header - fileout << GetParams().MessageStart() << block_size; - // Write block + fileout.write_large(DataStream(BLOCK_SERIALIZATION_HEADER_SIZE) << GetParams().MessageStart() << block_size); pos.nPos += BLOCK_SERIALIZATION_HEADER_SIZE; - fileout << TX_WITH_WITNESS(block); + // Write block + fileout.write_large(DataStream(block_size) << TX_WITH_WITNESS(block)); + return pos; }