mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-08 03:59:18 +02:00
optimization: Bulk serialization writes in WriteBlockUndo
and WriteBlock
Added `AutoFile::write_large` for batching obfuscation operations, so instead of copying the data and doing the xor in a 4096 byte array, we're doing it directly on the input. `DataStream` constructor was also added to enable presized serialization and writing in a single command. 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/bin/bench_bitcoin -filter='WriteBlockBench' -min-time=10000 > C++ compiler .......................... AppleClang 16.0.0.16000026 Before: | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 5,182,762.89 | 192.95 | 0.5% | 11.04 | `WriteBlockBench` After: | ns/op | op/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 1,791,444.08 | 558.21 | 0.9% | 11.08 | `WriteBlockBench` > 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 | `WriteBlockBench` 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 | `WriteBlockBench` Co-authored-by: Cory Fields <cory-nospam-@coryfields.com> Co-authored-by: Ryan Ofsky <ryan@ofsky.org>
This commit is contained in:
parent
b3ab94b12c
commit
5a1c2bd341
@ -959,14 +959,14 @@ bool BlockManager::WriteBlockUndo(const CBlockUndo& blockundo, BlockValidationSt
|
||||
}
|
||||
|
||||
// Write index header
|
||||
fileout << GetParams().MessageStart() << blockundo_size;
|
||||
BufferedFileW(fileout, HEADER_BYTE_SIZE) << GetParams().MessageStart() << blockundo_size;
|
||||
pos.nPos += HEADER_BYTE_SIZE;
|
||||
{
|
||||
// Calculate checksum
|
||||
HashWriter hasher{};
|
||||
hasher << block.pprev->GetBlockHash() << blockundo;
|
||||
// Write undo data & checksum
|
||||
fileout << blockundo << hasher.GetHash();
|
||||
BufferedFileW(fileout, 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)
|
||||
@ -1117,10 +1117,10 @@ FlatFilePos BlockManager::WriteBlock(const CBlock& block, int nHeight)
|
||||
}
|
||||
|
||||
// Write index header
|
||||
fileout << GetParams().MessageStart() << block_size;
|
||||
BufferedFileW(fileout, HEADER_BYTE_SIZE) << GetParams().MessageStart() << block_size;
|
||||
pos.nPos += HEADER_BYTE_SIZE;
|
||||
// Write block
|
||||
fileout << TX_WITH_WITNESS(block);
|
||||
BufferedFileW(fileout, block_size) << TX_WITH_WITNESS(block);
|
||||
return pos;
|
||||
}
|
||||
|
||||
|
@ -102,6 +102,16 @@ void AutoFile::write(Span<const std::byte> src)
|
||||
}
|
||||
}
|
||||
|
||||
void AutoFile::write_large(Span<std::byte> src)
|
||||
{
|
||||
if (!m_file) throw std::ios_base::failure("AutoFile::write_large: file handle is nullptr");
|
||||
util::Xor(src, m_xor, *m_position); // obfuscate in-place
|
||||
if (std::fwrite(src.data(), 1, src.size(), m_file) != src.size()) {
|
||||
throw std::ios_base::failure("AutoFile::write_large: write failed");
|
||||
}
|
||||
if (m_position) *m_position += src.size();
|
||||
}
|
||||
|
||||
bool AutoFile::Commit()
|
||||
{
|
||||
return ::FileCommit(m_file);
|
||||
|
@ -163,6 +163,7 @@ public:
|
||||
typedef vector_type::reverse_iterator reverse_iterator;
|
||||
|
||||
explicit DataStream() = default;
|
||||
explicit DataStream(size_type n) { reserve(n); }
|
||||
explicit DataStream(Span<const uint8_t> sp) : DataStream{AsBytes(sp)} {}
|
||||
explicit DataStream(Span<const value_type> sp) : vch(sp.data(), sp.data() + sp.size()) {}
|
||||
|
||||
@ -452,6 +453,7 @@ public:
|
||||
void read(Span<std::byte> dst);
|
||||
void ignore(size_t nSize);
|
||||
void write(Span<const std::byte> src);
|
||||
void write_large(Span<std::byte> src); // Note that src will be mutated
|
||||
|
||||
template <typename T>
|
||||
AutoFile& operator<<(const T& obj)
|
||||
@ -468,6 +470,32 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class BufferedFileW
|
||||
{
|
||||
AutoFile& m_file;
|
||||
uint32_t m_buffer_size;
|
||||
DataStream m_buf;
|
||||
|
||||
public:
|
||||
explicit BufferedFileW(AutoFile& file, const uint32_t buffer_size)
|
||||
: m_file(file), m_buffer_size{buffer_size}, m_buf{buffer_size} {}
|
||||
|
||||
~BufferedFileW()
|
||||
{
|
||||
Assert(m_buf.size() <= m_buffer_size);
|
||||
m_file.write_large(m_buf);
|
||||
}
|
||||
|
||||
void write(Span<const std::byte> src) { m_buf.write(src); }
|
||||
|
||||
template <typename T>
|
||||
BufferedFileW& operator<<(const T& obj)
|
||||
{
|
||||
Serialize(m_buf, obj);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
class BufferedFileR
|
||||
{
|
||||
DataStream m_buf;
|
||||
|
@ -572,11 +572,12 @@ BOOST_AUTO_TEST_CASE(streams_datastream_write_large)
|
||||
const uint32_t v1{m_rng.rand32()}, v2{m_rng.rand32()}, v3{m_rng.rand32()};
|
||||
const fs::path tmp_path{m_args.GetDataDirBase() / "test_datastream_write_large.bin"};
|
||||
|
||||
// Write out the values to file
|
||||
// Write out the values through a precisely sized BufferedFileW
|
||||
{
|
||||
AutoFile file{fsbridge::fopen(tmp_path, "w+b")};
|
||||
file << v1 << v2;
|
||||
file.write(AsBytes(Span{&v3, 1}));
|
||||
BufferedFileW f(file, sizeof(v1) + sizeof(v2) + sizeof(v3));
|
||||
f << v1 << v2;
|
||||
f.write(AsBytes(Span{&v3, 1}));
|
||||
}
|
||||
// Read back and verify using BufferedFileR
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user