shards crds values based on their hash prefix (#12187)
filter_crds_values checks every crds filter against every hash value: https://github.com/solana-labs/solana/blob/ee646aa7/core/src/crds_gossip_pull.rs#L432 which can be inefficient if the filter's bit-mask only matches small portion of the entire crds table. This commit shards crds values into separate tables based on shard_bits first bits of their hash prefix. Given a (mask, mask_bits) filter, filtering crds can be done by inspecting only relevant shards. If CrdsFilter.mask_bits <= shard_bits, then precisely only the crds values which match (mask, mask_bits) bit pattern are traversed. If CrdsFilter.mask_bits > shard_bits, then approximately only 1/2^shard_bits of crds values are inspected. Benchmarking on a gce cluster of 20 nodes, I see ~10% improvement in generate_pull_responses metric, but with larger clusters, crds table and 2^mask_bits are both larger, so the impact should be more significant.
This commit is contained in:
69
core/benches/crds_shards.rs
Normal file
69
core/benches/crds_shards.rs
Normal file
@@ -0,0 +1,69 @@
|
||||
#![feature(test)]
|
||||
|
||||
extern crate test;
|
||||
|
||||
use rand::{thread_rng, Rng};
|
||||
use solana_core::contact_info::ContactInfo;
|
||||
use solana_core::crds::VersionedCrdsValue;
|
||||
use solana_core::crds_shards::CrdsShards;
|
||||
use solana_core::crds_value::{CrdsData, CrdsValue};
|
||||
use solana_sdk::pubkey::Pubkey;
|
||||
use solana_sdk::timing::timestamp;
|
||||
use test::Bencher;
|
||||
|
||||
const CRDS_SHARDS_BITS: u32 = 8;
|
||||
|
||||
fn new_test_crds_value() -> VersionedCrdsValue {
|
||||
let data = CrdsData::ContactInfo(ContactInfo::new_localhost(&Pubkey::new_rand(), timestamp()));
|
||||
VersionedCrdsValue::new(timestamp(), CrdsValue::new_unsigned(data))
|
||||
}
|
||||
|
||||
fn bench_crds_shards_find(bencher: &mut Bencher, num_values: usize, mask_bits: u32) {
|
||||
let values: Vec<VersionedCrdsValue> = std::iter::repeat_with(new_test_crds_value)
|
||||
.take(num_values)
|
||||
.collect();
|
||||
let mut shards = CrdsShards::new(CRDS_SHARDS_BITS);
|
||||
for (index, value) in values.iter().enumerate() {
|
||||
assert!(shards.insert(index, value));
|
||||
}
|
||||
let mut rng = thread_rng();
|
||||
bencher.iter(|| {
|
||||
let mask = rng.gen();
|
||||
let _hits = shards.find(mask, mask_bits).count();
|
||||
});
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_0(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 0);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_1(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 1);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_3(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 3);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_5(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 5);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_7(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 7);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_8(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 8);
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn bench_crds_shards_find_9(bencher: &mut Bencher) {
|
||||
bench_crds_shards_find(bencher, 100_000, 9);
|
||||
}
|
Reference in New Issue
Block a user