Skip to content

Commit ecbf4ba

Browse files
committed
Merge bitcoin/bitcoin#29114: util: Faster std::byte (pre)vector (un)serialize
fab4169 Allow int8_t optimized vector serialization (MarcoFalke) facaa14 Faster std::byte (pre)vector (un)serialize (MarcoFalke) Pull request description: Currently, large vectors of `std::byte` are (un)serialized byte-by-byte, which is slow. Fix this, by enabling the already existing optimization for them. On my system this gives a 10x speedup for `./src/bench/bench_bitcoin --filter=PrevectorDeserializeTrivial`, when `std::byte` are used: ```diff diff --git a/src/bench/prevector.cpp b/src/bench/prevector.cpp index 2524e21..76b16bc34e 100644 --- a/src/bench/prevector.cpp +++ b/src/bench/prevector.cpp @@ -17,7 +17,7 @@ struct nontrivial_t { static_assert(!std::is_trivially_default_constructible<nontrivial_t>::value, "expected nontrivial_t to not be trivially constructible"); -typedef unsigned char trivial_t; +typedef std::byte trivial_t; static_assert(std::is_trivially_default_constructible<trivial_t>::value, "expected trivial_t to be trivially constructible"); ``` However, the optimization does not cover `signed char`. Fix that as well. ACKs for top commit: sipa: utACK fab4169 achow101: ACK fab4169 TheCharlatan: ACK fab4169 Tree-SHA512: a3e20f375fd1d0e0dedb827a8ce528de1173ea69660c8c891ad1343a86b422072f6505096fca0d3f8af4442fbe1378a02e32d5974919d4e88ff06934d0258cba
2 parents 0471aee + fab4169 commit ecbf4ba

File tree

2 files changed

+8
-10
lines changed

2 files changed

+8
-10
lines changed

src/serialize.h

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -710,14 +710,12 @@ template<typename Stream, typename C> void Unserialize(Stream& is, std::basic_st
710710

711711
/**
712712
* prevector
713-
* prevectors of unsigned char are a special case and are intended to be serialized as a single opaque blob.
714713
*/
715714
template<typename Stream, unsigned int N, typename T> inline void Serialize(Stream& os, const prevector<N, T>& v);
716715
template<typename Stream, unsigned int N, typename T> inline void Unserialize(Stream& is, prevector<N, T>& v);
717716

718717
/**
719718
* vector
720-
* vectors of unsigned char are a special case and are intended to be serialized as a single opaque blob.
721719
*/
722720
template<typename Stream, typename T, typename A> inline void Serialize(Stream& os, const std::vector<T, A>& v);
723721
template<typename Stream, typename T, typename A> inline void Unserialize(Stream& is, std::vector<T, A>& v);
@@ -820,10 +818,9 @@ void Unserialize(Stream& is, std::basic_string<C>& str)
820818
template <typename Stream, unsigned int N, typename T>
821819
void Serialize(Stream& os, const prevector<N, T>& v)
822820
{
823-
if constexpr (std::is_same_v<T, unsigned char>) {
821+
if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
824822
WriteCompactSize(os, v.size());
825-
if (!v.empty())
826-
os.write(MakeByteSpan(v));
823+
if (!v.empty()) os.write(MakeByteSpan(v));
827824
} else {
828825
Serialize(os, Using<VectorFormatter<DefaultFormatter>>(v));
829826
}
@@ -833,7 +830,7 @@ void Serialize(Stream& os, const prevector<N, T>& v)
833830
template <typename Stream, unsigned int N, typename T>
834831
void Unserialize(Stream& is, prevector<N, T>& v)
835832
{
836-
if constexpr (std::is_same_v<T, unsigned char>) {
833+
if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
837834
// Limit size per read so bogus size value won't cause out of memory
838835
v.clear();
839836
unsigned int nSize = ReadCompactSize(is);
@@ -856,10 +853,9 @@ void Unserialize(Stream& is, prevector<N, T>& v)
856853
template <typename Stream, typename T, typename A>
857854
void Serialize(Stream& os, const std::vector<T, A>& v)
858855
{
859-
if constexpr (std::is_same_v<T, unsigned char>) {
856+
if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
860857
WriteCompactSize(os, v.size());
861-
if (!v.empty())
862-
os.write(MakeByteSpan(v));
858+
if (!v.empty()) os.write(MakeByteSpan(v));
863859
} else if constexpr (std::is_same_v<T, bool>) {
864860
// A special case for std::vector<bool>, as dereferencing
865861
// std::vector<bool>::const_iterator does not result in a const bool&
@@ -877,7 +873,7 @@ void Serialize(Stream& os, const std::vector<T, A>& v)
877873
template <typename Stream, typename T, typename A>
878874
void Unserialize(Stream& is, std::vector<T, A>& v)
879875
{
880-
if constexpr (std::is_same_v<T, unsigned char>) {
876+
if constexpr (BasicByte<T>) { // Use optimized version for unformatted basic bytes
881877
// Limit size per read so bogus size value won't cause out of memory
882878
v.clear();
883879
unsigned int nSize = ReadCompactSize(is);

src/span.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,11 @@ Span<std::byte> MakeWritableByteSpan(V&& v) noexcept
287287
// Helper functions to safely cast basic byte pointers to unsigned char pointers.
288288
inline unsigned char* UCharCast(char* c) { return reinterpret_cast<unsigned char*>(c); }
289289
inline unsigned char* UCharCast(unsigned char* c) { return c; }
290+
inline unsigned char* UCharCast(signed char* c) { return reinterpret_cast<unsigned char*>(c); }
290291
inline unsigned char* UCharCast(std::byte* c) { return reinterpret_cast<unsigned char*>(c); }
291292
inline const unsigned char* UCharCast(const char* c) { return reinterpret_cast<const unsigned char*>(c); }
292293
inline const unsigned char* UCharCast(const unsigned char* c) { return c; }
294+
inline const unsigned char* UCharCast(const signed char* c) { return reinterpret_cast<const unsigned char*>(c); }
293295
inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_cast<const unsigned char*>(c); }
294296
// Helper concept for the basic byte types.
295297
template <typename B>

0 commit comments

Comments
 (0)