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.
70 lines
1.9 KiB
Rust
70 lines
1.9 KiB
Rust
#![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);
|
|
}
|