mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-06-06 19:23:41 +02:00
optimization: migrate SipHashUint256 to PresaltedSipHasher
Replaces standalone `SipHashUint256` with an `operator()` overload in `PresaltedSipHasher`. Updates all hasher classes (`SaltedUint256Hasher`, `SaltedTxidHasher`, `SaltedWtxidHasher`) to use `PresaltedSipHasher` internally, enabling the same constant-state caching optimization while keeping behavior unchanged. Benchmark was also adjusted to cache the salting part.
This commit is contained in:
@@ -193,13 +193,11 @@ static void SHA512(benchmark::Bench& bench)
|
|||||||
static void SipHash_32b(benchmark::Bench& bench)
|
static void SipHash_32b(benchmark::Bench& bench)
|
||||||
{
|
{
|
||||||
FastRandomContext rng{/*fDeterministic=*/true};
|
FastRandomContext rng{/*fDeterministic=*/true};
|
||||||
auto k0{rng.rand64()}, k1{rng.rand64()};
|
PresaltedSipHasher presalted_sip_hasher(rng.rand64(), rng.rand64());
|
||||||
auto val{rng.rand256()};
|
auto val{rng.rand256()};
|
||||||
auto i{0U};
|
auto i{0U};
|
||||||
bench.run([&] {
|
bench.run([&] {
|
||||||
ankerl::nanobench::doNotOptimizeAway(SipHashUint256(k0, k1, val));
|
ankerl::nanobench::doNotOptimizeAway(presalted_sip_hasher(val));
|
||||||
++k0;
|
|
||||||
++k1;
|
|
||||||
++i;
|
++i;
|
||||||
val.data()[i % uint256::size()] ^= i & 0xFF;
|
val.data()[i % uint256::size()] ^= i & 0xFF;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -42,7 +42,8 @@ void CBlockHeaderAndShortTxIDs::FillShortTxIDSelector() const {
|
|||||||
|
|
||||||
uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const {
|
uint64_t CBlockHeaderAndShortTxIDs::GetShortID(const Wtxid& wtxid) const {
|
||||||
static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids");
|
static_assert(SHORTTXIDS_LENGTH == 6, "shorttxids calculation assumes 6-byte shorttxids");
|
||||||
return SipHashUint256(shorttxidk0, shorttxidk1, wtxid.ToUint256()) & 0xffffffffffffL;
|
PresaltedSipHasher hasher(shorttxidk0, shorttxidk1); // TODO extract
|
||||||
|
return hasher(wtxid.ToUint256()) & 0xffffffffffffL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reconstructing a compact block is in the hot-path for block relay,
|
/* Reconstructing a compact block is in the hot-path for block relay,
|
||||||
|
|||||||
@@ -96,16 +96,11 @@ uint64_t CSipHasher::Finalize() const
|
|||||||
return v0 ^ v1 ^ v2 ^ v3;
|
return v0 ^ v1 ^ v2 ^ v3;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val)
|
uint64_t PresaltedSipHasher::operator()(const uint256& val) const noexcept
|
||||||
{
|
{
|
||||||
/* Specialized implementation for efficiency */
|
uint64_t v0 = v[0], v1 = v[1], v2 = v[2], v3 = v[3];
|
||||||
uint64_t d = val.GetUint64(0);
|
uint64_t d = val.GetUint64(0);
|
||||||
|
v3 ^= d;
|
||||||
// TODO moved in followup commit
|
|
||||||
uint64_t v0 = CSipHasher::C0 ^ k0;
|
|
||||||
uint64_t v1 = CSipHasher::C1 ^ k1;
|
|
||||||
uint64_t v2 = CSipHasher::C2 ^ k0;
|
|
||||||
uint64_t v3 = CSipHasher::C3 ^ k1 ^ d;
|
|
||||||
|
|
||||||
SIPROUND;
|
SIPROUND;
|
||||||
SIPROUND;
|
SIPROUND;
|
||||||
|
|||||||
@@ -37,18 +37,15 @@ public:
|
|||||||
uint64_t Finalize() const;
|
uint64_t Finalize() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Optimized SipHash-2-4 implementation for uint256.
|
/**
|
||||||
|
* Optimized SipHash-2-4 implementation for uint256.
|
||||||
*
|
*
|
||||||
* It is identical to:
|
* This class caches the initial SipHash v[0..3] state derived from (k0, k1)
|
||||||
* SipHasher(k0, k1)
|
* and implements a specialized hashing path for uint256 values, with or
|
||||||
* .Write(val.GetUint64(0))
|
* without an extra 32-bit word. The internal state is immutable, so
|
||||||
* .Write(val.GetUint64(1))
|
* PresaltedSipHasher instances can be reused for multiple hashes with the
|
||||||
* .Write(val.GetUint64(2))
|
* same key.
|
||||||
* .Write(val.GetUint64(3))
|
|
||||||
* .Finalize()
|
|
||||||
*/
|
*/
|
||||||
uint64_t SipHashUint256(uint64_t k0, uint64_t k1, const uint256& val);
|
|
||||||
|
|
||||||
class PresaltedSipHasher
|
class PresaltedSipHasher
|
||||||
{
|
{
|
||||||
uint64_t v[4];
|
uint64_t v[4];
|
||||||
@@ -61,6 +58,7 @@ public:
|
|||||||
v[3] = CSipHasher::C3 ^ k1;
|
v[3] = CSipHasher::C3 ^ k1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint64_t operator()(const uint256& val) const noexcept;
|
||||||
uint64_t operator()(const uint256& val, uint32_t extra) const noexcept;
|
uint64_t operator()(const uint256& val, uint32_t extra) const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ FUZZ_TARGET(integer, .init = initialize_integer)
|
|||||||
}
|
}
|
||||||
(void)MillisToTimeval(i64);
|
(void)MillisToTimeval(i64);
|
||||||
(void)SighashToStr(uch);
|
(void)SighashToStr(uch);
|
||||||
(void)SipHashUint256(u64, u64, u256);
|
(void)PresaltedSipHasher(u64, u64)(u256);
|
||||||
(void)PresaltedSipHasher(u64, u64)(u256, u32);
|
(void)PresaltedSipHasher(u64, u64)(u256, u32);
|
||||||
(void)ToLower(ch);
|
(void)ToLower(ch);
|
||||||
(void)ToUpper(ch);
|
(void)ToUpper(ch);
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(siphash)
|
|||||||
hasher.Write(0x2F2E2D2C2B2A2928ULL);
|
hasher.Write(0x2F2E2D2C2B2A2928ULL);
|
||||||
BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull);
|
BOOST_CHECK_EQUAL(hasher.Finalize(), 0xe612a3cb9ecba951ull);
|
||||||
|
|
||||||
BOOST_CHECK_EQUAL(SipHashUint256(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL, uint256{"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"}), 0x7127512f72f27cceull);
|
BOOST_CHECK_EQUAL(PresaltedSipHasher(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL)(uint256{"1f1e1d1c1b1a191817161514131211100f0e0d0c0b0a09080706050403020100"}), 0x7127512f72f27cceull);
|
||||||
|
|
||||||
// Check test vectors from spec, one byte at a time
|
// Check test vectors from spec, one byte at a time
|
||||||
CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
|
CSipHasher hasher2(0x0706050403020100ULL, 0x0F0E0D0C0B0A0908ULL);
|
||||||
@@ -128,9 +128,9 @@ BOOST_AUTO_TEST_CASE(siphash)
|
|||||||
// and the test would be affected by default tx version bumps if not fixed.
|
// and the test would be affected by default tx version bumps if not fixed.
|
||||||
tx.version = 1;
|
tx.version = 1;
|
||||||
ss << TX_WITH_WITNESS(tx);
|
ss << TX_WITH_WITNESS(tx);
|
||||||
BOOST_CHECK_EQUAL(SipHashUint256(1, 2, ss.GetHash()), 0x79751e980c2a0a35ULL);
|
BOOST_CHECK_EQUAL(PresaltedSipHasher(1, 2)(ss.GetHash()), 0x79751e980c2a0a35ULL);
|
||||||
|
|
||||||
// Check consistency between CSipHasher and SipHashUint256 and PresaltedSipHasher.
|
// Check consistency between CSipHasher and PresaltedSipHasher.
|
||||||
FastRandomContext ctx;
|
FastRandomContext ctx;
|
||||||
for (int i = 0; i < 16; ++i) {
|
for (int i = 0; i < 16; ++i) {
|
||||||
uint64_t k0 = ctx.rand64();
|
uint64_t k0 = ctx.rand64();
|
||||||
@@ -139,7 +139,7 @@ BOOST_AUTO_TEST_CASE(siphash)
|
|||||||
|
|
||||||
CSipHasher sip256(k0, k1);
|
CSipHasher sip256(k0, k1);
|
||||||
sip256.Write(x);
|
sip256.Write(x);
|
||||||
BOOST_CHECK_EQUAL(SipHashUint256(k0, k1, x), sip256.Finalize()); // TODO modified in follow-up commit
|
BOOST_CHECK_EQUAL(PresaltedSipHasher(k0, k1)(x), sip256.Finalize());
|
||||||
|
|
||||||
CSipHasher sip288 = sip256;
|
CSipHasher sip288 = sip256;
|
||||||
uint32_t n = ctx.rand32();
|
uint32_t n = ctx.rand32();
|
||||||
|
|||||||
@@ -7,17 +7,20 @@
|
|||||||
#include <span.h>
|
#include <span.h>
|
||||||
#include <util/hasher.h>
|
#include <util/hasher.h>
|
||||||
|
|
||||||
SaltedUint256Hasher::SaltedUint256Hasher() :
|
SaltedUint256Hasher::SaltedUint256Hasher() : m_hasher{
|
||||||
k0{FastRandomContext().rand64()},
|
FastRandomContext().rand64(),
|
||||||
k1{FastRandomContext().rand64()} {}
|
FastRandomContext().rand64()}
|
||||||
|
{}
|
||||||
|
|
||||||
SaltedTxidHasher::SaltedTxidHasher() :
|
SaltedTxidHasher::SaltedTxidHasher() : m_hasher{
|
||||||
k0{FastRandomContext().rand64()},
|
FastRandomContext().rand64(),
|
||||||
k1{FastRandomContext().rand64()} {}
|
FastRandomContext().rand64()}
|
||||||
|
{}
|
||||||
|
|
||||||
SaltedWtxidHasher::SaltedWtxidHasher() :
|
SaltedWtxidHasher::SaltedWtxidHasher() : m_hasher{
|
||||||
k0{FastRandomContext().rand64()},
|
FastRandomContext().rand64(),
|
||||||
k1{FastRandomContext().rand64()} {}
|
FastRandomContext().rand64()}
|
||||||
|
{}
|
||||||
|
|
||||||
SaltedOutpointHasher::SaltedOutpointHasher(bool deterministic) : m_hasher{
|
SaltedOutpointHasher::SaltedOutpointHasher(bool deterministic) : m_hasher{
|
||||||
deterministic ? 0x8e819f2607a18de6 : FastRandomContext().rand64(),
|
deterministic ? 0x8e819f2607a18de6 : FastRandomContext().rand64(),
|
||||||
|
|||||||
@@ -17,47 +17,43 @@
|
|||||||
|
|
||||||
class SaltedUint256Hasher
|
class SaltedUint256Hasher
|
||||||
{
|
{
|
||||||
private:
|
const PresaltedSipHasher m_hasher;
|
||||||
/** Salt */
|
|
||||||
const uint64_t k0, k1;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SaltedUint256Hasher();
|
SaltedUint256Hasher();
|
||||||
|
|
||||||
size_t operator()(const uint256& hash) const {
|
size_t operator()(const uint256& hash) const
|
||||||
return SipHashUint256(k0, k1, hash);
|
{
|
||||||
|
return m_hasher(hash);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class SaltedTxidHasher
|
class SaltedTxidHasher
|
||||||
{
|
{
|
||||||
private:
|
const PresaltedSipHasher m_hasher;
|
||||||
/** Salt */
|
|
||||||
const uint64_t k0, k1;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SaltedTxidHasher();
|
SaltedTxidHasher();
|
||||||
|
|
||||||
size_t operator()(const Txid& txid) const {
|
size_t operator()(const Txid& txid) const
|
||||||
return SipHashUint256(k0, k1, txid.ToUint256());
|
{
|
||||||
|
return m_hasher(txid.ToUint256());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class SaltedWtxidHasher
|
class SaltedWtxidHasher
|
||||||
{
|
{
|
||||||
private:
|
const PresaltedSipHasher m_hasher;
|
||||||
/** Salt */
|
|
||||||
const uint64_t k0, k1;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SaltedWtxidHasher();
|
SaltedWtxidHasher();
|
||||||
|
|
||||||
size_t operator()(const Wtxid& wtxid) const {
|
size_t operator()(const Wtxid& wtxid) const
|
||||||
return SipHashUint256(k0, k1, wtxid.ToUint256());
|
{
|
||||||
|
return m_hasher(wtxid.ToUint256());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class SaltedOutpointHasher
|
class SaltedOutpointHasher
|
||||||
{
|
{
|
||||||
const PresaltedSipHasher m_hasher;
|
const PresaltedSipHasher m_hasher;
|
||||||
@@ -80,8 +76,7 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct FilterHeaderHasher
|
struct FilterHeaderHasher {
|
||||||
{
|
|
||||||
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
|
size_t operator()(const uint256& hash) const { return ReadLE64(hash.begin()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user