mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
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`
This commit is contained in:
parent
f40e3487d5
commit
073e28b1e9
11 changed files with 84 additions and 10 deletions
|
@ -52,7 +52,7 @@ static void DeserializeBlock(benchmark::Bench& bench)
|
|||
{
|
||||
DataStream stream(benchmark::data::block413567);
|
||||
std::byte a{0};
|
||||
stream.write({&a, 1}); // Prevent compaction
|
||||
stream.write(std::span{&a, 1}); // Prevent compaction
|
||||
|
||||
bench.unit("block").run([&] {
|
||||
CBlock block;
|
||||
|
@ -66,7 +66,7 @@ static void DeserializeAndCheckBlock(benchmark::Bench& bench)
|
|||
{
|
||||
DataStream stream(benchmark::data::block413567);
|
||||
std::byte a{0};
|
||||
stream.write({&a, 1}); // Prevent compaction
|
||||
stream.write(std::span{&a, 1}); // Prevent compaction
|
||||
|
||||
ArgsManager bench_args;
|
||||
const auto chainParams = CreateChainParams(bench_args, ChainType::MAIN);
|
||||
|
|
|
@ -33,7 +33,7 @@ struct TestBlockAndIndex {
|
|||
{
|
||||
DataStream stream{benchmark::data::block413567};
|
||||
std::byte a{0};
|
||||
stream.write({&a, 1}); // Prevent compaction
|
||||
stream.write(std::span{&a, 1}); // Prevent compaction
|
||||
|
||||
stream >> TX_WITH_WITNESS(block);
|
||||
|
||||
|
|
|
@ -723,6 +723,21 @@ CSHA256& CSHA256::Write(const unsigned char* data, size_t len)
|
|||
}
|
||||
return *this;
|
||||
}
|
||||
CSHA256& CSHA256::Write(unsigned char data)
|
||||
{
|
||||
size_t bufsize = bytes % 64;
|
||||
|
||||
// Add the single byte to the buffer
|
||||
buf[bufsize] = data;
|
||||
bytes += 1;
|
||||
|
||||
if (bufsize == 63) {
|
||||
// Process the buffer if full
|
||||
Transform(s, buf, 1);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void CSHA256::Finalize(unsigned char hash[OUTPUT_SIZE])
|
||||
{
|
||||
|
|
|
@ -22,6 +22,7 @@ public:
|
|||
|
||||
CSHA256();
|
||||
CSHA256& Write(const unsigned char* data, size_t len);
|
||||
CSHA256& Write(unsigned char data);
|
||||
void Finalize(unsigned char hash[OUTPUT_SIZE]);
|
||||
CSHA256& Reset();
|
||||
};
|
||||
|
|
24
src/hash.h
24
src/hash.h
|
@ -38,6 +38,10 @@ public:
|
|||
sha.Write(input.data(), input.size());
|
||||
return *this;
|
||||
}
|
||||
CHash256& Write(std::span<const unsigned char, 1> input) {
|
||||
sha.Write(input[0]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CHash256& Reset() {
|
||||
sha.Reset();
|
||||
|
@ -63,6 +67,10 @@ public:
|
|||
sha.Write(input.data(), input.size());
|
||||
return *this;
|
||||
}
|
||||
CHash160& Write(std::span<const unsigned char, 1> input) {
|
||||
sha.Write(input[0]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
CHash160& Reset() {
|
||||
sha.Reset();
|
||||
|
@ -107,6 +115,10 @@ public:
|
|||
{
|
||||
ctx.Write(UCharCast(src.data()), src.size());
|
||||
}
|
||||
void write(std::span<const std::byte, 1> src)
|
||||
{
|
||||
ctx.Write(*UCharCast(&src[0]));
|
||||
}
|
||||
|
||||
/** Compute the double-SHA256 hash of all data written to this object.
|
||||
*
|
||||
|
@ -160,13 +172,18 @@ public:
|
|||
m_source.read(dst);
|
||||
this->write(dst);
|
||||
}
|
||||
void read(std::span<std::byte, 1> dst)
|
||||
{
|
||||
m_source.read(dst);
|
||||
this->write(std::span<const std::byte, 1>{dst});
|
||||
}
|
||||
|
||||
void ignore(size_t num_bytes)
|
||||
{
|
||||
std::byte data[1024];
|
||||
while (num_bytes > 0) {
|
||||
size_t now = std::min<size_t>(num_bytes, 1024);
|
||||
read({data, now});
|
||||
read(std::span{data, now});
|
||||
num_bytes -= now;
|
||||
}
|
||||
}
|
||||
|
@ -194,6 +211,11 @@ public:
|
|||
m_source.write(src);
|
||||
HashWriter::write(src);
|
||||
}
|
||||
void write(std::span<const std::byte, 1> src)
|
||||
{
|
||||
m_source.write(src);
|
||||
HashWriter::write(src);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
HashedSourceWriter& operator<<(const T& obj)
|
||||
|
|
|
@ -1112,6 +1112,10 @@ public:
|
|||
{
|
||||
this->nSize += src.size();
|
||||
}
|
||||
void write(std::span<const std::byte, 1>)
|
||||
{
|
||||
this->nSize += 1;
|
||||
}
|
||||
|
||||
/** Pretend _nSize bytes are written, without specifying them. */
|
||||
void seek(size_t _nSize)
|
||||
|
@ -1161,7 +1165,9 @@ public:
|
|||
template <typename U> ParamsStream& operator<<(const U& obj) { ::Serialize(*this, obj); return *this; }
|
||||
template <typename U> ParamsStream& operator>>(U&& obj) { ::Unserialize(*this, obj); return *this; }
|
||||
void write(std::span<const std::byte> src) { GetStream().write(src); }
|
||||
void write(std::span<const std::byte, 1> src) { GetStream().write(src); }
|
||||
void read(std::span<std::byte> dst) { GetStream().read(dst); }
|
||||
void read(std::span<std::byte, 1> dst) { GetStream().read(dst); }
|
||||
void ignore(size_t num) { GetStream().ignore(num); }
|
||||
bool eof() const { return GetStream().eof(); }
|
||||
size_t size() const { return GetStream().size(); }
|
||||
|
|
|
@ -64,6 +64,13 @@ void AutoFile::read(std::span<std::byte> dst)
|
|||
}
|
||||
}
|
||||
|
||||
void AutoFile::read(std::span<std::byte, 1> dst)
|
||||
{
|
||||
if (detail_fread(dst) != 1) {
|
||||
throw std::ios_base::failure(feof() ? "AutoFile::read: end of file" : "AutoFile::read: fread failed");
|
||||
}
|
||||
}
|
||||
|
||||
void AutoFile::ignore(size_t nSize)
|
||||
{
|
||||
if (!m_file) throw std::ios_base::failure("AutoFile::ignore: file handle is nullptr");
|
||||
|
@ -97,6 +104,12 @@ void AutoFile::write(std::span<const std::byte> src)
|
|||
}
|
||||
}
|
||||
|
||||
void AutoFile::write(std::span<const std::byte, 1> src)
|
||||
{
|
||||
std::byte temp_byte = src[0];
|
||||
write_buffer(std::span(&temp_byte, 1));
|
||||
}
|
||||
|
||||
void AutoFile::write_buffer(std::span<std::byte> src)
|
||||
{
|
||||
if (!m_file) throw std::ios_base::failure("AutoFile::write_buffer: file handle is nullptr");
|
||||
|
|
|
@ -83,6 +83,17 @@ public:
|
|||
}
|
||||
nPos += src.size();
|
||||
}
|
||||
void write(std::span<const std::byte, 1> src)
|
||||
{
|
||||
assert(nPos <= vchData.size());
|
||||
const auto byte{*UCharCast(&src[0])};
|
||||
if (nPos < vchData.size()) {
|
||||
vchData[nPos] = byte;
|
||||
} else {
|
||||
vchData.push_back(byte);
|
||||
}
|
||||
nPos += 1;
|
||||
}
|
||||
template <typename T>
|
||||
VectorWriter& operator<<(const T& obj)
|
||||
{
|
||||
|
@ -254,6 +265,10 @@ public:
|
|||
// Write to the end of the buffer
|
||||
vch.insert(vch.end(), src.begin(), src.end());
|
||||
}
|
||||
void write(std::span<const value_type, 1> src)
|
||||
{
|
||||
vch.push_back(src[0]);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
DataStream& operator<<(const T& obj)
|
||||
|
@ -453,8 +468,10 @@ public:
|
|||
// Stream subset
|
||||
//
|
||||
void read(std::span<std::byte> dst);
|
||||
void read(std::span<std::byte, 1> dst);
|
||||
void ignore(size_t nSize);
|
||||
void write(std::span<const std::byte> src);
|
||||
void write(std::span<const std::byte, 1> src);
|
||||
|
||||
template <typename T>
|
||||
AutoFile& operator<<(const T& obj)
|
||||
|
|
|
@ -1079,7 +1079,7 @@ BOOST_AUTO_TEST_CASE(sha256d64)
|
|||
in[j] = m_rng.randbits(8);
|
||||
}
|
||||
for (int j = 0; j < i; ++j) {
|
||||
CHash256().Write({in + 64 * j, 64}).Finalize({out1 + 32 * j, 32});
|
||||
CHash256().Write(std::span{in + 64 * j, 64}).Finalize({out1 + 32 * j, 32});
|
||||
}
|
||||
SHA256D64(out2, in, i);
|
||||
BOOST_CHECK(memcmp(out1, out2, 32 * i) == 0);
|
||||
|
|
|
@ -29,14 +29,14 @@ FUZZ_TARGET(autofile)
|
|||
[&] {
|
||||
std::array<std::byte, 4096> arr{};
|
||||
try {
|
||||
auto_file.read({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
|
||||
auto_file.read(std::span{arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
|
||||
} catch (const std::ios_base::failure&) {
|
||||
}
|
||||
},
|
||||
[&] {
|
||||
const std::array<std::byte, 4096> arr{};
|
||||
try {
|
||||
auto_file.write({arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
|
||||
auto_file.write(std::span{arr.data(), fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, 4096)});
|
||||
} catch (const std::ios_base::failure&) {
|
||||
}
|
||||
},
|
||||
|
|
|
@ -26,9 +26,9 @@ BOOST_AUTO_TEST_CASE(xor_file)
|
|||
{
|
||||
// Check errors for missing file
|
||||
AutoFile xor_file{raw_file("rb"), xor_pat};
|
||||
BOOST_CHECK_EXCEPTION(xor_file << std::byte{}, std::ios_base::failure, HasReason{"AutoFile::write: file handle is nullpt"});
|
||||
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"AutoFile::read: file handle is nullpt"});
|
||||
BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"AutoFile::ignore: file handle is nullpt"});
|
||||
BOOST_CHECK_EXCEPTION(xor_file << std::byte{}, std::ios_base::failure, HasReason{"file handle is nullpt"});
|
||||
BOOST_CHECK_EXCEPTION(xor_file >> std::byte{}, std::ios_base::failure, HasReason{"file handle is nullpt"});
|
||||
BOOST_CHECK_EXCEPTION(xor_file.ignore(1), std::ios_base::failure, HasReason{"file handle is nullpt"});
|
||||
}
|
||||
{
|
||||
#ifdef __MINGW64__
|
||||
|
|
Loading…
Add table
Reference in a new issue