mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-04-29 14:59:39 -04:00
optimization: merge SizeComputer
specializations and add new overloads
Endianness doesn’t affect final size, so skip it in `SizeComputer`. Fold existing overloads into one implementation, short‑circuiting logic when only the serialized size is needed. > cmake -B build -DBUILD_BENCH=ON -DCMAKE_BUILD_TYPE=Release && cmake --build build -j$(nproc) && build/src/bench/bench_bitcoin -filter='SizeComputerBlock|SerializeBlock' --min-time=10000 > C compiler ............................ AppleClang 16.0.0.16000026 | ns/block | block/s | err% | total | benchmark |--------------------:|--------------------:|--------:|----------:|:---------- | 191,652.29 | 5,217.78 | 0.4% | 10.96 | `SerializeBlock` | 10,323.55 | 96,865.92 | 0.2% | 11.01 | `SizeComputerBlock` > C++ compiler .......................... GNU 13.3.0 | ns/block | block/s | err% | ins/block | cyc/block | IPC | bra/block | miss% | total | benchmark |--------------------:|--------------------:|--------:|----------------:|----------------:|-------:|---------------:|--------:|----------:|:---------- | 614,847.32 | 1,626.42 | 0.0% | 8,015,883.64 | 2,207,628.07 | 3.631 | 1,517,035.62 | 0.5% | 10.56 | `SerializeBlock` | 26,020.31 | 38,431.52 | 0.0% | 159,390.03 | 93,438.33 | 1.706 | 42,131.03 | 0.9% | 11.00 | `SizeComputerBlock`
This commit is contained in:
parent
cbdb759d0e
commit
f40e3487d5
1 changed files with 73 additions and 42 deletions
115
src/serialize.h
115
src/serialize.h
|
@ -48,6 +48,16 @@ static const unsigned int MAX_VECTOR_ALLOCATE = 5000000;
|
|||
struct deserialize_type {};
|
||||
constexpr deserialize_type deserialize {};
|
||||
|
||||
class SizeComputer;
|
||||
|
||||
//! Check if type contains a stream by seeing if it has a GetStream() method.
|
||||
template<typename T>
|
||||
concept ContainsStream = requires(T t) { t.GetStream(); };
|
||||
|
||||
template<typename T>
|
||||
concept ContainsSizeComputer = ContainsStream<T> &&
|
||||
std::is_same_v<std::remove_reference_t<decltype(std::declval<T>().GetStream())>, SizeComputer>;
|
||||
|
||||
/*
|
||||
* Lowest-level serialization and conversion.
|
||||
*/
|
||||
|
@ -107,8 +117,6 @@ template<typename Stream> inline uint64_t ser_readdata64(Stream &s)
|
|||
}
|
||||
|
||||
|
||||
class SizeComputer;
|
||||
|
||||
/**
|
||||
* Convert any argument to a reference to X, maintaining constness.
|
||||
*
|
||||
|
@ -248,7 +256,9 @@ concept ByteOrIntegral = std::is_same_v<T, std::byte> ||
|
|||
template <typename Stream, CharNotInt8 V> void Serialize(Stream&, V) = delete; // char serialization forbidden. Use uint8_t or int8_t
|
||||
template <typename Stream, ByteOrIntegral T> void Serialize(Stream& s, T a)
|
||||
{
|
||||
if constexpr (sizeof(T) == 1) {
|
||||
if constexpr (ContainsSizeComputer<Stream>) {
|
||||
s.GetStream().seek(sizeof(T));
|
||||
} else if constexpr (sizeof(T) == 1) {
|
||||
ser_writedata8(s, static_cast<uint8_t>(a)); // (u)int8_t or std::byte or bool
|
||||
} else if constexpr (sizeof(T) == 2) {
|
||||
ser_writedata16(s, static_cast<uint16_t>(a)); // (u)int16_t
|
||||
|
@ -259,10 +269,38 @@ template <typename Stream, ByteOrIntegral T> void Serialize(Stream& s, T a)
|
|||
ser_writedata64(s, static_cast<uint64_t>(a)); // (u)int64_t
|
||||
}
|
||||
}
|
||||
template <typename Stream, BasicByte B, int N> void Serialize(Stream& s, const B (&a)[N]) { s.write(MakeByteSpan(a)); }
|
||||
template <typename Stream, BasicByte B, std::size_t N> void Serialize(Stream& s, const std::array<B, N>& a) { s.write(MakeByteSpan(a)); }
|
||||
template <typename Stream, BasicByte B, std::size_t N> void Serialize(Stream& s, std::span<B, N> span) { s.write(std::as_bytes(span)); }
|
||||
template <typename Stream, BasicByte B> void Serialize(Stream& s, std::span<B> span) { s.write(std::as_bytes(span)); }
|
||||
template <typename Stream, BasicByte B, int N> void Serialize(Stream& s, const B (&a)[N])
|
||||
{
|
||||
if constexpr (ContainsSizeComputer<Stream>) {
|
||||
s.GetStream().seek(N);
|
||||
} else {
|
||||
s.write(MakeByteSpan(a));
|
||||
}
|
||||
}
|
||||
template <typename Stream, BasicByte B, std::size_t N> void Serialize(Stream& s, const std::array<B, N>& a)
|
||||
{
|
||||
if constexpr (ContainsSizeComputer<Stream>) {
|
||||
s.GetStream().seek(N);
|
||||
} else {
|
||||
s.write(MakeByteSpan(a));
|
||||
}
|
||||
}
|
||||
template <typename Stream, BasicByte B, std::size_t N> void Serialize(Stream& s, std::span<B, N> span)
|
||||
{
|
||||
if constexpr (ContainsSizeComputer<Stream>) {
|
||||
s.GetStream().seek(N);
|
||||
} else {
|
||||
s.write(std::as_bytes(span));
|
||||
}
|
||||
}
|
||||
template <typename Stream, BasicByte B> void Serialize(Stream& s, std::span<B> span)
|
||||
{
|
||||
if constexpr (ContainsSizeComputer<Stream>) {
|
||||
s.GetStream().seek(span.size());
|
||||
} else {
|
||||
s.write(std::as_bytes(span));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Stream, CharNotInt8 V> void Unserialize(Stream&, V) = delete; // char serialization forbidden. Use uint8_t or int8_t
|
||||
template <typename Stream, ByteOrIntegral T> void Unserialize(Stream& s, T& a)
|
||||
|
@ -299,12 +337,14 @@ constexpr inline unsigned int GetSizeOfCompactSize(uint64_t nSize)
|
|||
else return sizeof(unsigned char) + sizeof(uint64_t);
|
||||
}
|
||||
|
||||
inline void WriteCompactSize(SizeComputer& os, uint64_t nSize);
|
||||
|
||||
template<typename Stream>
|
||||
void WriteCompactSize(Stream& os, uint64_t nSize)
|
||||
{
|
||||
if (nSize < 253)
|
||||
if constexpr (ContainsSizeComputer<Stream>)
|
||||
{
|
||||
os.GetStream().seek(GetSizeOfCompactSize(nSize));
|
||||
}
|
||||
else if (nSize < 253)
|
||||
{
|
||||
ser_writedata8(os, nSize);
|
||||
}
|
||||
|
@ -411,7 +451,7 @@ struct CheckVarIntMode {
|
|||
};
|
||||
|
||||
template<VarIntMode Mode, typename I>
|
||||
inline unsigned int GetSizeOfVarInt(I n)
|
||||
constexpr unsigned int GetSizeOfVarInt(I n)
|
||||
{
|
||||
CheckVarIntMode<Mode, I>();
|
||||
int nRet = 0;
|
||||
|
@ -424,25 +464,26 @@ inline unsigned int GetSizeOfVarInt(I n)
|
|||
return nRet;
|
||||
}
|
||||
|
||||
template<typename I>
|
||||
inline void WriteVarInt(SizeComputer& os, I n);
|
||||
|
||||
template<typename Stream, VarIntMode Mode, typename I>
|
||||
void WriteVarInt(Stream& os, I n)
|
||||
{
|
||||
CheckVarIntMode<Mode, I>();
|
||||
unsigned char tmp[(sizeof(n)*8+6)/7];
|
||||
int len=0;
|
||||
while(true) {
|
||||
tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00);
|
||||
if (n <= 0x7F)
|
||||
break;
|
||||
n = (n >> 7) - 1;
|
||||
len++;
|
||||
if constexpr (ContainsSizeComputer<Stream>) {
|
||||
os.GetStream().seek(GetSizeOfVarInt<Mode, I>(n));
|
||||
} else {
|
||||
CheckVarIntMode<Mode, I>();
|
||||
unsigned char tmp[(sizeof(n)*8+6)/7];
|
||||
int len=0;
|
||||
while(true) {
|
||||
tmp[len] = (n & 0x7F) | (len ? 0x80 : 0x00);
|
||||
if (n <= 0x7F)
|
||||
break;
|
||||
n = (n >> 7) - 1;
|
||||
len++;
|
||||
}
|
||||
do {
|
||||
ser_writedata8(os, tmp[len]);
|
||||
} while(len--);
|
||||
}
|
||||
do {
|
||||
ser_writedata8(os, tmp[len]);
|
||||
} while(len--);
|
||||
}
|
||||
|
||||
template<typename Stream, VarIntMode Mode, typename I>
|
||||
|
@ -531,7 +572,9 @@ struct CustomUintFormatter
|
|||
template <typename Stream, typename I> void Ser(Stream& s, I v)
|
||||
{
|
||||
if (v < 0 || v > MAX) throw std::ios_base::failure("CustomUintFormatter value out of range");
|
||||
if (BigEndian) {
|
||||
if constexpr (ContainsSizeComputer<Stream>) {
|
||||
s.GetStream().seek(Bytes);
|
||||
} else if (BigEndian) {
|
||||
uint64_t raw = htobe64_internal(v);
|
||||
s.write(std::as_bytes(std::span{&raw, 1}).template last<Bytes>());
|
||||
} else {
|
||||
|
@ -1062,6 +1105,9 @@ protected:
|
|||
public:
|
||||
SizeComputer() = default;
|
||||
|
||||
SizeComputer& GetStream() { return *this; }
|
||||
const SizeComputer& GetStream() const { return *this; };
|
||||
|
||||
void write(std::span<const std::byte> src)
|
||||
{
|
||||
this->nSize += src.size();
|
||||
|
@ -1085,27 +1131,12 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
template<typename I>
|
||||
inline void WriteVarInt(SizeComputer &s, I n)
|
||||
{
|
||||
s.seek(GetSizeOfVarInt<I>(n));
|
||||
}
|
||||
|
||||
inline void WriteCompactSize(SizeComputer &s, uint64_t nSize)
|
||||
{
|
||||
s.seek(GetSizeOfCompactSize(nSize));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
size_t GetSerializeSize(const T& t)
|
||||
{
|
||||
return (SizeComputer() << t).size();
|
||||
}
|
||||
|
||||
//! Check if type contains a stream by seeing if has a GetStream() method.
|
||||
template<typename T>
|
||||
concept ContainsStream = requires(T t) { t.GetStream(); };
|
||||
|
||||
/** Wrapper that overrides the GetParams() function of a stream. */
|
||||
template <typename SubStream, typename Params>
|
||||
class ParamsStream
|
||||
|
|
Loading…
Add table
Reference in a new issue