Skip to content

Commit 1067771

Browse files
committed
Merge bitcoin/bitcoin#30396: random: add benchmarks and drop unnecessary Shuffle function
6ecda04 random: drop ad-hoc Shuffle in favor of std::shuffle (Pieter Wuille) da28a26 bench random: benchmark more functions, and add InsecureRandomContext (Pieter Wuille) 0a9bbc6 random bench refactor: move to new bench/random.cpp (Pieter Wuille) Pull request description: This adds benchmarks for various operations on `FastRandomContext` and `InsecureRandomContext`, and then removes the ad-hoc `Shuffle` functions, now that it appears that standard library `std::shuffle` has comparable performance. The other reason for keeping `Shuffle`, namely the fact that libstdc++ used self-move (which debug mode panics on) has been fixed as well (see bitcoin/bitcoin#29625 (comment)). ACKs for top commit: achow101: ACK 6ecda04 hodlinator: ACK 6ecda04 dergoegge: Code review ACK 6ecda04 Tree-SHA512: 2560b7312410581ff2b9bd0716e0f1558d910b5eadb9544785c972384985ac0f11f72d6b2797cfe2e7eb71fa57c30cffd98cc009cb4ee87a18b1524694211417
2 parents c51c694 + 6ecda04 commit 1067771

16 files changed

+123
-65
lines changed

src/Makefile.bench.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ bench_bench_bitcoin_SOURCES = \
4949
bench/poly1305.cpp \
5050
bench/pool.cpp \
5151
bench/prevector.cpp \
52+
bench/random.cpp \
5253
bench/readblock.cpp \
5354
bench/rollingbloom.cpp \
5455
bench/rpc_blockchain.cpp \

src/bench/crypto_hash.cpp

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -196,22 +196,6 @@ static void SipHash_32b(benchmark::Bench& bench)
196196
});
197197
}
198198

199-
static void FastRandom_32bit(benchmark::Bench& bench)
200-
{
201-
FastRandomContext rng(true);
202-
bench.run([&] {
203-
rng.rand32();
204-
});
205-
}
206-
207-
static void FastRandom_1bit(benchmark::Bench& bench)
208-
{
209-
FastRandomContext rng(true);
210-
bench.run([&] {
211-
rng.randbool();
212-
});
213-
}
214-
215199
static void MuHash(benchmark::Bench& bench)
216200
{
217201
MuHash3072 acc;
@@ -274,8 +258,6 @@ BENCHMARK(SHA256D64_1024_STANDARD, benchmark::PriorityLevel::HIGH);
274258
BENCHMARK(SHA256D64_1024_SSE4, benchmark::PriorityLevel::HIGH);
275259
BENCHMARK(SHA256D64_1024_AVX2, benchmark::PriorityLevel::HIGH);
276260
BENCHMARK(SHA256D64_1024_SHANI, benchmark::PriorityLevel::HIGH);
277-
BENCHMARK(FastRandom_32bit, benchmark::PriorityLevel::HIGH);
278-
BENCHMARK(FastRandom_1bit, benchmark::PriorityLevel::HIGH);
279261

280262
BENCHMARK(MuHash, benchmark::PriorityLevel::HIGH);
281263
BENCHMARK(MuHashMul, benchmark::PriorityLevel::HIGH);

src/bench/random.cpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// Copyright (c) The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <bench/bench.h>
6+
#include <random.h>
7+
8+
#include <cstdint>
9+
#include <numeric>
10+
11+
namespace {
12+
13+
template<typename RNG>
14+
void BenchRandom_rand64(benchmark::Bench& bench, RNG&& rng) noexcept
15+
{
16+
bench.batch(1).unit("number").run([&] {
17+
rng.rand64();
18+
});
19+
}
20+
21+
template<typename RNG>
22+
void BenchRandom_rand32(benchmark::Bench& bench, RNG&& rng) noexcept
23+
{
24+
bench.batch(1).unit("number").run([&] {
25+
rng.rand32();
26+
});
27+
}
28+
29+
template<typename RNG>
30+
void BenchRandom_randbool(benchmark::Bench& bench, RNG&& rng) noexcept
31+
{
32+
bench.batch(1).unit("number").run([&] {
33+
rng.randbool();
34+
});
35+
}
36+
37+
template<typename RNG>
38+
void BenchRandom_randbits(benchmark::Bench& bench, RNG&& rng) noexcept
39+
{
40+
bench.batch(64).unit("number").run([&] {
41+
for (int i = 1; i <= 64; ++i) {
42+
rng.randbits(i);
43+
}
44+
});
45+
}
46+
47+
template<int RANGE, typename RNG>
48+
void BenchRandom_randrange(benchmark::Bench& bench, RNG&& rng) noexcept
49+
{
50+
bench.batch(RANGE).unit("number").run([&] {
51+
for (int i = 1; i <= RANGE; ++i) {
52+
rng.randrange(i);
53+
}
54+
});
55+
}
56+
57+
template<int RANGE, typename RNG>
58+
void BenchRandom_stdshuffle(benchmark::Bench& bench, RNG&& rng) noexcept
59+
{
60+
uint64_t data[RANGE];
61+
std::iota(std::begin(data), std::end(data), uint64_t(0));
62+
bench.batch(RANGE).unit("number").run([&] {
63+
std::shuffle(std::begin(data), std::end(data), rng);
64+
});
65+
}
66+
67+
void FastRandom_rand64(benchmark::Bench& bench) { BenchRandom_rand64(bench, FastRandomContext(true)); }
68+
void FastRandom_rand32(benchmark::Bench& bench) { BenchRandom_rand32(bench, FastRandomContext(true)); }
69+
void FastRandom_randbool(benchmark::Bench& bench) { BenchRandom_randbool(bench, FastRandomContext(true)); }
70+
void FastRandom_randbits(benchmark::Bench& bench) { BenchRandom_randbits(bench, FastRandomContext(true)); }
71+
void FastRandom_randrange100(benchmark::Bench& bench) { BenchRandom_randrange<100>(bench, FastRandomContext(true)); }
72+
void FastRandom_randrange1000(benchmark::Bench& bench) { BenchRandom_randrange<1000>(bench, FastRandomContext(true)); }
73+
void FastRandom_randrange1000000(benchmark::Bench& bench) { BenchRandom_randrange<1000000>(bench, FastRandomContext(true)); }
74+
void FastRandom_stdshuffle100(benchmark::Bench& bench) { BenchRandom_stdshuffle<100>(bench, FastRandomContext(true)); }
75+
76+
void InsecureRandom_rand64(benchmark::Bench& bench) { BenchRandom_rand64(bench, InsecureRandomContext(251438)); }
77+
void InsecureRandom_rand32(benchmark::Bench& bench) { BenchRandom_rand32(bench, InsecureRandomContext(251438)); }
78+
void InsecureRandom_randbool(benchmark::Bench& bench) { BenchRandom_randbool(bench, InsecureRandomContext(251438)); }
79+
void InsecureRandom_randbits(benchmark::Bench& bench) { BenchRandom_randbits(bench, InsecureRandomContext(251438)); }
80+
void InsecureRandom_randrange100(benchmark::Bench& bench) { BenchRandom_randrange<100>(bench, InsecureRandomContext(251438)); }
81+
void InsecureRandom_randrange1000(benchmark::Bench& bench) { BenchRandom_randrange<1000>(bench, InsecureRandomContext(251438)); }
82+
void InsecureRandom_randrange1000000(benchmark::Bench& bench) { BenchRandom_randrange<1000000>(bench, InsecureRandomContext(251438)); }
83+
void InsecureRandom_stdshuffle100(benchmark::Bench& bench) { BenchRandom_stdshuffle<100>(bench, InsecureRandomContext(251438)); }
84+
85+
} // namespace
86+
87+
BENCHMARK(FastRandom_rand64, benchmark::PriorityLevel::HIGH);
88+
BENCHMARK(FastRandom_rand32, benchmark::PriorityLevel::HIGH);
89+
BENCHMARK(FastRandom_randbool, benchmark::PriorityLevel::HIGH);
90+
BENCHMARK(FastRandom_randbits, benchmark::PriorityLevel::HIGH);
91+
BENCHMARK(FastRandom_randrange100, benchmark::PriorityLevel::HIGH);
92+
BENCHMARK(FastRandom_randrange1000, benchmark::PriorityLevel::HIGH);
93+
BENCHMARK(FastRandom_randrange1000000, benchmark::PriorityLevel::HIGH);
94+
BENCHMARK(FastRandom_stdshuffle100, benchmark::PriorityLevel::HIGH);
95+
96+
BENCHMARK(InsecureRandom_rand64, benchmark::PriorityLevel::HIGH);
97+
BENCHMARK(InsecureRandom_rand32, benchmark::PriorityLevel::HIGH);
98+
BENCHMARK(InsecureRandom_randbool, benchmark::PriorityLevel::HIGH);
99+
BENCHMARK(InsecureRandom_randbits, benchmark::PriorityLevel::HIGH);
100+
BENCHMARK(InsecureRandom_randrange100, benchmark::PriorityLevel::HIGH);
101+
BENCHMARK(InsecureRandom_randrange1000, benchmark::PriorityLevel::HIGH);
102+
BENCHMARK(InsecureRandom_randrange1000000, benchmark::PriorityLevel::HIGH);
103+
BENCHMARK(InsecureRandom_stdshuffle100, benchmark::PriorityLevel::HIGH);

src/net.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
415415
if (pszDest) {
416416
std::vector<CService> resolved{Lookup(pszDest, default_port, fNameLookup && !HaveNameProxy(), 256)};
417417
if (!resolved.empty()) {
418-
Shuffle(resolved.begin(), resolved.end(), FastRandomContext());
418+
std::shuffle(resolved.begin(), resolved.end(), FastRandomContext());
419419
// If the connection is made by name, it can be the case that the name resolves to more than one address.
420420
// We don't want to connect any more of them if we are already connected to one
421421
for (const auto& r : resolved) {
@@ -2212,7 +2212,7 @@ void CConnman::ThreadDNSAddressSeed()
22122212

22132213
FastRandomContext rng;
22142214
std::vector<std::string> seeds = m_params.DNSSeeds();
2215-
Shuffle(seeds.begin(), seeds.end(), rng);
2215+
std::shuffle(seeds.begin(), seeds.end(), rng);
22162216
int seeds_right_now = 0; // Number of seeds left before testing if we have enough connections
22172217

22182218
if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) {
@@ -2439,7 +2439,7 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
24392439
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
24402440
{
24412441
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
2442-
Shuffle(nets.begin(), nets.end(), FastRandomContext());
2442+
std::shuffle(nets.begin(), nets.end(), FastRandomContext());
24432443

24442444
LOCK(m_nodes_mutex);
24452445
for (const auto net : nets) {

src/net.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1625,7 +1625,7 @@ class CConnman
16251625
}
16261626
}
16271627
if (shuffle) {
1628-
Shuffle(m_nodes_copy.begin(), m_nodes_copy.end(), FastRandomContext{});
1628+
std::shuffle(m_nodes_copy.begin(), m_nodes_copy.end(), FastRandomContext{});
16291629
}
16301630
}
16311631

src/net_processing.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3339,7 +3339,7 @@ std::optional<PeerManagerImpl::PackageToValidate> PeerManagerImpl::Find1P1CPacka
33393339
// Create a random permutation of the indices.
33403340
std::vector<size_t> tx_indices(cpfp_candidates_different_peer.size());
33413341
std::iota(tx_indices.begin(), tx_indices.end(), 0);
3342-
Shuffle(tx_indices.begin(), tx_indices.end(), m_rng);
3342+
std::shuffle(tx_indices.begin(), tx_indices.end(), m_rng);
33433343

33443344
for (const auto index : tx_indices) {
33453345
// If we already tried a package and failed for any reason, the combined hash was
@@ -4106,7 +4106,7 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
41064106
const bool rate_limited = !pfrom.HasPermission(NetPermissionFlags::Addr);
41074107
uint64_t num_proc = 0;
41084108
uint64_t num_rate_limit = 0;
4109-
Shuffle(vAddr.begin(), vAddr.end(), m_rng);
4109+
std::shuffle(vAddr.begin(), vAddr.end(), m_rng);
41104110
for (CAddress& addr : vAddr)
41114111
{
41124112
if (interruptMsgProc)

src/random.h

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -458,29 +458,6 @@ inline uint256 GetRandHash() noexcept
458458
return hash;
459459
}
460460

461-
/** More efficient than using std::shuffle on a FastRandomContext.
462-
*
463-
* This is more efficient as std::shuffle will consume entropy in groups of
464-
* 64 bits at the time and throw away most.
465-
*
466-
* This also works around a bug in libstdc++ std::shuffle that may cause
467-
* type::operator=(type&&) to be invoked on itself, which the library's
468-
* debug mode detects and panics on. This is a known issue, see
469-
* https://stackoverflow.com/questions/22915325/avoiding-self-assignment-in-stdshuffle
470-
*/
471-
template <typename I, RandomNumberGenerator R>
472-
void Shuffle(I first, I last, R&& rng)
473-
{
474-
while (first != last) {
475-
size_t j = rng.randrange(last - first);
476-
if (j) {
477-
using std::swap;
478-
swap(*first, *(first + j));
479-
}
480-
++first;
481-
}
482-
}
483-
484461
/* ============================= MISCELLANEOUS TEST-ONLY FUNCTIONS ============================= */
485462

486463
/** Check that OS randomness is available and returning the requested number

src/rpc/rawtransaction.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1790,8 +1790,8 @@ static RPCHelpMan joinpsbts()
17901790
std::iota(output_indices.begin(), output_indices.end(), 0);
17911791

17921792
// Shuffle input and output indices lists
1793-
Shuffle(input_indices.begin(), input_indices.end(), FastRandomContext());
1794-
Shuffle(output_indices.begin(), output_indices.end(), FastRandomContext());
1793+
std::shuffle(input_indices.begin(), input_indices.end(), FastRandomContext());
1794+
std::shuffle(output_indices.begin(), output_indices.end(), FastRandomContext());
17951795

17961796
PartiallySignedTransaction shuffled_psbt;
17971797
shuffled_psbt.tx = CMutableTransaction();

src/test/fuzz/random.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,5 @@ FUZZ_TARGET(random)
2626
(void)fast_random_context();
2727

2828
std::vector<int64_t> integrals = ConsumeRandomLengthIntegralVector<int64_t>(fuzzed_data_provider);
29-
Shuffle(integrals.begin(), integrals.end(), fast_random_context);
3029
std::shuffle(integrals.begin(), integrals.end(), fast_random_context);
3130
}

src/test/miniscript_tests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ void TestSatisfy(const KeyConverter& converter, const std::string& testcase, con
346346
auto challenges = FindChallenges(node); // Find all challenges in the generated miniscript.
347347
std::vector<Challenge> challist(challenges.begin(), challenges.end());
348348
for (int iter = 0; iter < 3; ++iter) {
349-
Shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
349+
std::shuffle(challist.begin(), challist.end(), g_insecure_rand_ctx);
350350
Satisfier satisfier(converter.MsContext());
351351
TestSignatureChecker checker(satisfier);
352352
bool prev_mal_success = false, prev_nonmal_success = false;

0 commit comments

Comments
 (0)