Skip to content

Commit 6ecda04

Browse files
committed
random: drop ad-hoc Shuffle in favor of std::shuffle
Benchmarks show it is no longer faster with modern standard C++ libraries, and the debug-mode failure due to self-move has been fixed as well.
1 parent da28a26 commit 6ecda04

14 files changed

+19
-61
lines changed

src/bench/random.cpp

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -54,16 +54,6 @@ void BenchRandom_randrange(benchmark::Bench& bench, RNG&& rng) noexcept
5454
});
5555
}
5656

57-
template<int RANGE, typename RNG>
58-
void BenchRandom_Shuffle(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-
Shuffle(std::begin(data), std::end(data), rng);
64-
});
65-
}
66-
6757
template<int RANGE, typename RNG>
6858
void BenchRandom_stdshuffle(benchmark::Bench& bench, RNG&& rng) noexcept
6959
{
@@ -81,7 +71,6 @@ void FastRandom_randbits(benchmark::Bench& bench) { BenchRandom_randbits(bench,
8171
void FastRandom_randrange100(benchmark::Bench& bench) { BenchRandom_randrange<100>(bench, FastRandomContext(true)); }
8272
void FastRandom_randrange1000(benchmark::Bench& bench) { BenchRandom_randrange<1000>(bench, FastRandomContext(true)); }
8373
void FastRandom_randrange1000000(benchmark::Bench& bench) { BenchRandom_randrange<1000000>(bench, FastRandomContext(true)); }
84-
void FastRandom_Shuffle100(benchmark::Bench& bench) { BenchRandom_Shuffle<100>(bench, FastRandomContext(true)); }
8574
void FastRandom_stdshuffle100(benchmark::Bench& bench) { BenchRandom_stdshuffle<100>(bench, FastRandomContext(true)); }
8675

8776
void InsecureRandom_rand64(benchmark::Bench& bench) { BenchRandom_rand64(bench, InsecureRandomContext(251438)); }
@@ -91,7 +80,6 @@ void InsecureRandom_randbits(benchmark::Bench& bench) { BenchRandom_randbits(ben
9180
void InsecureRandom_randrange100(benchmark::Bench& bench) { BenchRandom_randrange<100>(bench, InsecureRandomContext(251438)); }
9281
void InsecureRandom_randrange1000(benchmark::Bench& bench) { BenchRandom_randrange<1000>(bench, InsecureRandomContext(251438)); }
9382
void InsecureRandom_randrange1000000(benchmark::Bench& bench) { BenchRandom_randrange<1000000>(bench, InsecureRandomContext(251438)); }
94-
void InsecureRandom_Shuffle100(benchmark::Bench& bench) { BenchRandom_Shuffle<100>(bench, InsecureRandomContext(251438)); }
9583
void InsecureRandom_stdshuffle100(benchmark::Bench& bench) { BenchRandom_stdshuffle<100>(bench, InsecureRandomContext(251438)); }
9684

9785
} // namespace
@@ -103,7 +91,6 @@ BENCHMARK(FastRandom_randbits, benchmark::PriorityLevel::HIGH);
10391
BENCHMARK(FastRandom_randrange100, benchmark::PriorityLevel::HIGH);
10492
BENCHMARK(FastRandom_randrange1000, benchmark::PriorityLevel::HIGH);
10593
BENCHMARK(FastRandom_randrange1000000, benchmark::PriorityLevel::HIGH);
106-
BENCHMARK(FastRandom_Shuffle100, benchmark::PriorityLevel::HIGH);
10794
BENCHMARK(FastRandom_stdshuffle100, benchmark::PriorityLevel::HIGH);
10895

10996
BENCHMARK(InsecureRandom_rand64, benchmark::PriorityLevel::HIGH);
@@ -113,5 +100,4 @@ BENCHMARK(InsecureRandom_randbits, benchmark::PriorityLevel::HIGH);
113100
BENCHMARK(InsecureRandom_randrange100, benchmark::PriorityLevel::HIGH);
114101
BENCHMARK(InsecureRandom_randrange1000, benchmark::PriorityLevel::HIGH);
115102
BENCHMARK(InsecureRandom_randrange1000000, benchmark::PriorityLevel::HIGH);
116-
BENCHMARK(InsecureRandom_Shuffle100, benchmark::PriorityLevel::HIGH);
117103
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) {
@@ -2208,7 +2208,7 @@ void CConnman::ThreadDNSAddressSeed()
22082208

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

22142214
if (gArgs.GetBoolArg("-forcednsseed", DEFAULT_FORCEDNSSEED)) {
@@ -2435,7 +2435,7 @@ bool CConnman::MultipleManualOrFullOutboundConns(Network net) const
24352435
bool CConnman::MaybePickPreferredNetwork(std::optional<Network>& network)
24362436
{
24372437
std::array<Network, 5> nets{NET_IPV4, NET_IPV6, NET_ONION, NET_I2P, NET_CJDNS};
2438-
Shuffle(nets.begin(), nets.end(), FastRandomContext());
2438+
std::shuffle(nets.begin(), nets.end(), FastRandomContext());
24392439

24402440
LOCK(m_nodes_mutex);
24412441
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;

src/test/net_peer_eviction_tests.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ bool IsProtected(int num_peers,
3131
for (NodeEvictionCandidate& candidate : candidates) {
3232
candidate_setup_fn(candidate);
3333
}
34-
Shuffle(candidates.begin(), candidates.end(), random_context);
34+
std::shuffle(candidates.begin(), candidates.end(), random_context);
3535

3636
const size_t size{candidates.size()};
3737
const size_t expected{size - size / 2}; // Expect half the candidates will be protected.
@@ -572,7 +572,7 @@ BOOST_AUTO_TEST_CASE(peer_protection_test)
572572
// Returns true if any of the node ids in node_ids are selected for eviction.
573573
bool IsEvicted(std::vector<NodeEvictionCandidate> candidates, const std::unordered_set<NodeId>& node_ids, FastRandomContext& random_context)
574574
{
575-
Shuffle(candidates.begin(), candidates.end(), random_context);
575+
std::shuffle(candidates.begin(), candidates.end(), random_context);
576576
const std::optional<NodeId> evicted_node_id = SelectNodeToEvict(std::move(candidates));
577577
if (!evicted_node_id) {
578578
return false;

src/test/random_tests.cpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,10 +206,6 @@ BOOST_AUTO_TEST_CASE(stdrandom_test)
206206
for (int j = 1; j <= 10; ++j) {
207207
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
208208
}
209-
Shuffle(test.begin(), test.end(), ctx);
210-
for (int j = 1; j <= 10; ++j) {
211-
BOOST_CHECK(std::find(test.begin(), test.end(), j) != test.end());
212-
}
213209
}
214210
}
215211

@@ -220,7 +216,7 @@ BOOST_AUTO_TEST_CASE(shuffle_stat_test)
220216
uint32_t counts[5 * 5 * 5 * 5 * 5] = {0};
221217
for (int i = 0; i < 12000; ++i) {
222218
int data[5] = {0, 1, 2, 3, 4};
223-
Shuffle(std::begin(data), std::end(data), ctx);
219+
std::shuffle(std::begin(data), std::end(data), ctx);
224220
int pos = data[0] + data[1] * 5 + data[2] * 25 + data[3] * 125 + data[4] * 625;
225221
++counts[pos];
226222
}

0 commit comments

Comments
 (0)