implements an unbiased weighted shuffle using binary indexed tree (#18343)
Current implementation of weighted_shuffle: https://github.com/solana-labs/solana/blob/b08f8bd1b/gossip/src/weighted_shuffle.rs#L11-L37 uses a heuristic which results in biased samples. For example, if the weights are [1, 10, 100], then the 3rd index should come first 100 times more often than the 1st index. However, weighted_shuffle is picking the 3rd index 200+ times more often than the 1st index, showing a disproportional bias in favor of higher weights. This commit implements weighted shuffle using binary indexed tree to maintain cumulative sum of weights while sampling. The resulting samples are demonstrably unbiased and precisely proportional to the weights. Additionally the iterator interface allows to skip computations when not all indices are processed. Of the use cases of weighted_shuffle, changing turbine code requires feature-gating to keep the cluster in sync. That is not updated in this commit, but can be done together with future updates to turbine.
This commit is contained in:
39
gossip/benches/weighted_shuffle.rs
Normal file
39
gossip/benches/weighted_shuffle.rs
Normal file
@ -0,0 +1,39 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use {
|
||||
rand::{Rng, SeedableRng},
|
||||
rand_chacha::ChaChaRng,
|
||||
solana_gossip::weighted_shuffle::{weighted_shuffle, WeightedShuffle},
|
||||
std::iter::repeat_with,
|
||||
test::Bencher,
|
||||
};
|
||||
|
||||
fn make_weights<R: Rng>(rng: &mut R) -> Vec<u64> {
|
||||
repeat_with(|| rng.gen_range(1, 100)).take(1000).collect()
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_weighted_shuffle_old(bencher: &mut Bencher) {
|
||||
let mut seed = [0u8; 32];
|
||||
let mut rng = rand::thread_rng();
|
||||
let weights = make_weights(&mut rng);
|
||||
bencher.iter(|| {
|
||||
rng.fill(&mut seed[..]);
|
||||
weighted_shuffle(&weights, seed);
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_weighted_shuffle_new(bencher: &mut Bencher) {
|
||||
let mut seed = [0u8; 32];
|
||||
let mut rng = rand::thread_rng();
|
||||
let weights = make_weights(&mut rng);
|
||||
bencher.iter(|| {
|
||||
rng.fill(&mut seed[..]);
|
||||
WeightedShuffle::new(&mut ChaChaRng::from_seed(seed), &weights)
|
||||
.unwrap()
|
||||
.collect::<Vec<_>>()
|
||||
});
|
||||
}
|
Reference in New Issue
Block a user