bank: prime new executor cache entry use-counts
This commit is contained in:
parent
2759cb860b
commit
4ce48307bb
|
@ -66,6 +66,7 @@ use {
|
|||
dashmap::DashMap,
|
||||
itertools::Itertools,
|
||||
log::*,
|
||||
rand::Rng,
|
||||
rayon::{
|
||||
iter::{IntoParallelIterator, IntoParallelRefIterator, ParallelIterator},
|
||||
ThreadPool, ThreadPoolBuilder,
|
||||
|
@ -144,7 +145,7 @@ use {
|
|||
collections::{HashMap, HashSet},
|
||||
convert::{TryFrom, TryInto},
|
||||
fmt, mem,
|
||||
ops::RangeInclusive,
|
||||
ops::{Div, RangeInclusive},
|
||||
path::PathBuf,
|
||||
ptr,
|
||||
rc::Rc,
|
||||
|
@ -390,29 +391,32 @@ impl CachedExecutors {
|
|||
entry
|
||||
} else {
|
||||
saturating_add_assign!(self.stats.misses, 1);
|
||||
if self.executors.len() >= self.max {
|
||||
let mut least = u64::MAX;
|
||||
let default_key = Pubkey::default();
|
||||
let mut least_key = &default_key;
|
||||
|
||||
for (key, entry) in self.executors.iter() {
|
||||
let mut counts = self
|
||||
.executors
|
||||
.iter()
|
||||
.map(|(key, entry)| {
|
||||
let count = entry.prev_epoch_count + entry.epoch_count.load(Relaxed);
|
||||
if count < least {
|
||||
least = count;
|
||||
least_key = key;
|
||||
}
|
||||
(key, count)
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
counts.sort_unstable_by_key(|(_, count)| *count);
|
||||
|
||||
let primer_count = Self::get_primer_count(counts.as_slice());
|
||||
|
||||
if self.executors.len() >= self.max {
|
||||
if let Some(least_key) = counts.first().map(|least| *least.0) {
|
||||
let _ = self.executors.remove(&least_key);
|
||||
self.stats
|
||||
.evictions
|
||||
.entry(least_key)
|
||||
.and_modify(|c| saturating_add_assign!(*c, 1))
|
||||
.or_insert(1);
|
||||
}
|
||||
let least_key = *least_key;
|
||||
let _ = self.executors.remove(&least_key);
|
||||
self.stats
|
||||
.evictions
|
||||
.entry(least_key)
|
||||
.and_modify(|c| saturating_add_assign!(*c, 1))
|
||||
.or_insert(1);
|
||||
}
|
||||
|
||||
CachedExecutorsEntry {
|
||||
prev_epoch_count: 0,
|
||||
epoch_count: AtomicU64::new(0),
|
||||
epoch_count: AtomicU64::new(primer_count),
|
||||
executor,
|
||||
}
|
||||
};
|
||||
|
@ -422,6 +426,38 @@ impl CachedExecutors {
|
|||
fn remove(&mut self, pubkey: &Pubkey) {
|
||||
let _ = self.executors.remove(pubkey);
|
||||
}
|
||||
|
||||
fn get_primer_count_upper_bound_inclusive(counts: &[(&Pubkey, u64)]) -> u64 {
|
||||
const PRIMER_COUNT_TARGET_PERCENTILE: u64 = 85;
|
||||
#[allow(clippy::assertions_on_constants)]
|
||||
{
|
||||
assert!(PRIMER_COUNT_TARGET_PERCENTILE <= 100);
|
||||
}
|
||||
// Executor use-frequencies are assumed to fit a Pareto distribution. Choose an
|
||||
// upper-bound for our primer count as the actual count at the target rank to avoid
|
||||
// an upward bias
|
||||
|
||||
let target_index = u64::try_from(counts.len().saturating_sub(1))
|
||||
.ok()
|
||||
.and_then(|counts| {
|
||||
let index = counts
|
||||
.saturating_mul(PRIMER_COUNT_TARGET_PERCENTILE)
|
||||
.div(100); // switch to u64::saturating_div once stable
|
||||
usize::try_from(index).ok()
|
||||
})
|
||||
.unwrap_or(0);
|
||||
|
||||
counts
|
||||
.get(target_index)
|
||||
.map(|(_, count)| *count)
|
||||
.unwrap_or(0)
|
||||
}
|
||||
|
||||
fn get_primer_count(counts: &[(&Pubkey, u64)]) -> u64 {
|
||||
let max_primer_count = Self::get_primer_count_upper_bound_inclusive(counts);
|
||||
let mut rng = rand::thread_rng();
|
||||
rng.gen_range(0, max_primer_count.saturating_add(1))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -12806,19 +12842,25 @@ pub(crate) mod tests {
|
|||
assert!(cache.get(&key1).is_some());
|
||||
assert!(cache.get(&key2).is_some());
|
||||
cache.put(&key4, executor.clone());
|
||||
assert!(cache.get(&key1).is_some());
|
||||
assert!(cache.get(&key2).is_some());
|
||||
assert!(cache.get(&key3).is_none());
|
||||
assert!(cache.get(&key4).is_some());
|
||||
let num_retained = [&key1, &key2, &key3]
|
||||
.iter()
|
||||
.map(|key| cache.get(key))
|
||||
.flatten()
|
||||
.count();
|
||||
assert_eq!(num_retained, 2);
|
||||
|
||||
assert!(cache.get(&key4).is_some());
|
||||
assert!(cache.get(&key4).is_some());
|
||||
assert!(cache.get(&key4).is_some());
|
||||
cache.put(&key3, executor.clone());
|
||||
assert!(cache.get(&key1).is_some());
|
||||
assert!(cache.get(&key2).is_none());
|
||||
assert!(cache.get(&key3).is_some());
|
||||
assert!(cache.get(&key4).is_some());
|
||||
let num_retained = [&key1, &key2, &key4]
|
||||
.iter()
|
||||
.map(|key| cache.get(key))
|
||||
.flatten()
|
||||
.count();
|
||||
assert_eq!(num_retained, 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -12847,12 +12889,23 @@ pub(crate) mod tests {
|
|||
Arc::make_mut(&mut cache).put(&key4, executor.clone());
|
||||
|
||||
assert!(cache.get(&key4).is_some());
|
||||
assert!(cache.get(&key3).is_none());
|
||||
let num_retained = [&key1, &key2, &key3]
|
||||
.iter()
|
||||
.map(|key| cache.get(key))
|
||||
.flatten()
|
||||
.count();
|
||||
assert_eq!(num_retained, 2);
|
||||
|
||||
Arc::make_mut(&mut cache).put(&key1, executor.clone());
|
||||
Arc::make_mut(&mut cache).put(&key3, executor.clone());
|
||||
assert!(cache.get(&key1).is_some());
|
||||
assert!(cache.get(&key4).is_none());
|
||||
assert!(cache.get(&key3).is_some());
|
||||
let num_retained = [&key2, &key4]
|
||||
.iter()
|
||||
.map(|key| cache.get(key))
|
||||
.flatten()
|
||||
.count();
|
||||
assert_eq!(num_retained, 1);
|
||||
|
||||
cache = cache.clone_with_epoch(2);
|
||||
assert!(cache.current_epoch == 2);
|
||||
|
@ -15398,4 +15451,24 @@ pub(crate) mod tests {
|
|||
))
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_executor_cache_get_primer_count_upper_bound_inclusive() {
|
||||
let pubkey = Pubkey::default();
|
||||
let v = [];
|
||||
assert_eq!(
|
||||
CachedExecutors::get_primer_count_upper_bound_inclusive(&v),
|
||||
0
|
||||
);
|
||||
let v = [(&pubkey, 1)];
|
||||
assert_eq!(
|
||||
CachedExecutors::get_primer_count_upper_bound_inclusive(&v),
|
||||
1
|
||||
);
|
||||
let v = (0u64..10).map(|i| (&pubkey, i)).collect::<Vec<_>>();
|
||||
assert_eq!(
|
||||
CachedExecutors::get_primer_count_upper_bound_inclusive(v.as_slice()),
|
||||
7
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue