bump executor cache max entries (#25600)
* executor-cache: unify `remove` calls * executor-cache: metrics - add one-hit-wonder counter * executor-cache: bump max entries to 256
This commit is contained in:
parent
e4409a87fe
commit
4e6b276f6c
|
@ -279,6 +279,7 @@ mod executor_cache {
|
||||||
pub evictions: HashMap<Pubkey, u64>,
|
pub evictions: HashMap<Pubkey, u64>,
|
||||||
pub insertions: AtomicU64,
|
pub insertions: AtomicU64,
|
||||||
pub replacements: AtomicU64,
|
pub replacements: AtomicU64,
|
||||||
|
pub one_hit_wonders: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stats {
|
impl Stats {
|
||||||
|
@ -287,6 +288,7 @@ mod executor_cache {
|
||||||
let misses = self.misses.load(Relaxed);
|
let misses = self.misses.load(Relaxed);
|
||||||
let insertions = self.insertions.load(Relaxed);
|
let insertions = self.insertions.load(Relaxed);
|
||||||
let replacements = self.replacements.load(Relaxed);
|
let replacements = self.replacements.load(Relaxed);
|
||||||
|
let one_hit_wonders = self.one_hit_wonders.load(Relaxed);
|
||||||
let evictions: u64 = self.evictions.values().sum();
|
let evictions: u64 = self.evictions.values().sum();
|
||||||
datapoint_info!(
|
datapoint_info!(
|
||||||
"bank-executor-cache-stats",
|
"bank-executor-cache-stats",
|
||||||
|
@ -296,10 +298,11 @@ mod executor_cache {
|
||||||
("evictions", evictions, i64),
|
("evictions", evictions, i64),
|
||||||
("insertions", insertions, i64),
|
("insertions", insertions, i64),
|
||||||
("replacements", replacements, i64),
|
("replacements", replacements, i64),
|
||||||
|
("one_hit_wonders", one_hit_wonders, i64),
|
||||||
);
|
);
|
||||||
debug!(
|
debug!(
|
||||||
"Executor Cache Stats -- Hits: {}, Misses: {}, Evictions: {}, Insertions: {}, Replacements: {}",
|
"Executor Cache Stats -- Hits: {}, Misses: {}, Evictions: {}, Insertions: {}, Replacements: {}, One-Hit-Wonders: {}",
|
||||||
hits, misses, evictions, insertions, replacements,
|
hits, misses, evictions, insertions, replacements, one_hit_wonders,
|
||||||
);
|
);
|
||||||
if log_enabled!(log::Level::Trace) && !self.evictions.is_empty() {
|
if log_enabled!(log::Level::Trace) && !self.evictions.is_empty() {
|
||||||
let mut evictions = self.evictions.iter().collect::<Vec<_>>();
|
let mut evictions = self.evictions.iter().collect::<Vec<_>>();
|
||||||
|
@ -323,12 +326,13 @@ mod executor_cache {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_CACHED_EXECUTORS: usize = 100; // 10 MB assuming programs are around 100k
|
const MAX_CACHED_EXECUTORS: usize = 256;
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct CachedExecutorsEntry {
|
struct CachedExecutorsEntry {
|
||||||
prev_epoch_count: u64,
|
prev_epoch_count: u64,
|
||||||
epoch_count: AtomicU64,
|
epoch_count: AtomicU64,
|
||||||
executor: Arc<dyn Executor>,
|
executor: Arc<dyn Executor>,
|
||||||
|
hit_count: AtomicU64,
|
||||||
}
|
}
|
||||||
/// LFU Cache of executors with single-epoch memory of usage counts
|
/// LFU Cache of executors with single-epoch memory of usage counts
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -366,6 +370,7 @@ impl Clone for CachedExecutors {
|
||||||
prev_epoch_count: entry.prev_epoch_count,
|
prev_epoch_count: entry.prev_epoch_count,
|
||||||
epoch_count: AtomicU64::new(entry.epoch_count.load(Relaxed)),
|
epoch_count: AtomicU64::new(entry.epoch_count.load(Relaxed)),
|
||||||
executor: entry.executor.clone(),
|
executor: entry.executor.clone(),
|
||||||
|
hit_count: AtomicU64::new(entry.hit_count.load(Relaxed)),
|
||||||
};
|
};
|
||||||
(key, entry)
|
(key, entry)
|
||||||
});
|
});
|
||||||
|
@ -390,6 +395,7 @@ impl CachedExecutors {
|
||||||
prev_epoch_count: entry.epoch_count.load(Relaxed),
|
prev_epoch_count: entry.epoch_count.load(Relaxed),
|
||||||
epoch_count: AtomicU64::default(),
|
epoch_count: AtomicU64::default(),
|
||||||
executor: entry.executor.clone(),
|
executor: entry.executor.clone(),
|
||||||
|
hit_count: AtomicU64::new(entry.hit_count.load(Relaxed)),
|
||||||
};
|
};
|
||||||
(key, entry)
|
(key, entry)
|
||||||
});
|
});
|
||||||
|
@ -414,6 +420,7 @@ impl CachedExecutors {
|
||||||
if let Some(entry) = self.executors.get(pubkey) {
|
if let Some(entry) = self.executors.get(pubkey) {
|
||||||
self.stats.hits.fetch_add(1, Relaxed);
|
self.stats.hits.fetch_add(1, Relaxed);
|
||||||
entry.epoch_count.fetch_add(1, Relaxed);
|
entry.epoch_count.fetch_add(1, Relaxed);
|
||||||
|
entry.hit_count.fetch_add(1, Relaxed);
|
||||||
Some(entry.executor.clone())
|
Some(entry.executor.clone())
|
||||||
} else {
|
} else {
|
||||||
self.stats.misses.fetch_add(1, Relaxed);
|
self.stats.misses.fetch_add(1, Relaxed);
|
||||||
|
@ -425,7 +432,7 @@ impl CachedExecutors {
|
||||||
let mut new_executors: Vec<_> = executors
|
let mut new_executors: Vec<_> = executors
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(key, executor)| {
|
.filter_map(|(key, executor)| {
|
||||||
if let Some(mut entry) = self.executors.remove(key) {
|
if let Some(mut entry) = self.remove(key) {
|
||||||
self.stats.replacements.fetch_add(1, Relaxed);
|
self.stats.replacements.fetch_add(1, Relaxed);
|
||||||
entry.executor = executor.clone();
|
entry.executor = executor.clone();
|
||||||
let _ = self.executors.insert(**key, entry);
|
let _ = self.executors.insert(**key, entry);
|
||||||
|
@ -457,7 +464,7 @@ impl CachedExecutors {
|
||||||
.map(|least| *least.0)
|
.map(|least| *least.0)
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
for least_key in least_keys.drain(..) {
|
for least_key in least_keys.drain(..) {
|
||||||
let _ = self.executors.remove(&least_key);
|
let _ = self.remove(&least_key);
|
||||||
self.stats
|
self.stats
|
||||||
.evictions
|
.evictions
|
||||||
.entry(least_key)
|
.entry(least_key)
|
||||||
|
@ -471,14 +478,21 @@ impl CachedExecutors {
|
||||||
prev_epoch_count: 0,
|
prev_epoch_count: 0,
|
||||||
epoch_count: AtomicU64::new(primer_count),
|
epoch_count: AtomicU64::new(primer_count),
|
||||||
executor: executor.clone(),
|
executor: executor.clone(),
|
||||||
|
hit_count: AtomicU64::new(1),
|
||||||
};
|
};
|
||||||
let _ = self.executors.insert(*key, entry);
|
let _ = self.executors.insert(*key, entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove(&mut self, pubkey: &Pubkey) {
|
fn remove(&mut self, pubkey: &Pubkey) -> Option<CachedExecutorsEntry> {
|
||||||
let _ = self.executors.remove(pubkey);
|
let maybe_entry = self.executors.remove(pubkey);
|
||||||
|
if let Some(entry) = maybe_entry.as_ref() {
|
||||||
|
if entry.hit_count.load(Relaxed) == 1 {
|
||||||
|
self.stats.one_hit_wonders.fetch_add(1, Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
maybe_entry
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear(&mut self) {
|
fn clear(&mut self) {
|
||||||
|
@ -4181,7 +4195,7 @@ impl Bank {
|
||||||
/// Remove an executor from the bank's cache
|
/// Remove an executor from the bank's cache
|
||||||
fn remove_executor(&self, pubkey: &Pubkey) {
|
fn remove_executor(&self, pubkey: &Pubkey) {
|
||||||
let mut cache = self.cached_executors.write().unwrap();
|
let mut cache = self.cached_executors.write().unwrap();
|
||||||
Arc::make_mut(&mut cache).remove(pubkey);
|
let _ = Arc::make_mut(&mut cache).remove(pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear_executors(&self) {
|
pub fn clear_executors(&self) {
|
||||||
|
@ -14509,6 +14523,39 @@ pub(crate) mod tests {
|
||||||
assert!(cache.get(&entries[1].0).is_some());
|
assert!(cache.get(&entries[1].0).is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cached_executors_one_hit_wonder_counter() {
|
||||||
|
let mut cache = CachedExecutors::new(1, 0);
|
||||||
|
|
||||||
|
let one_hit_wonder = Pubkey::new_unique();
|
||||||
|
let popular = Pubkey::new_unique();
|
||||||
|
let executor: Arc<dyn Executor> = Arc::new(TestExecutor {});
|
||||||
|
|
||||||
|
// make sure we're starting from where we think we are
|
||||||
|
assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 0);
|
||||||
|
|
||||||
|
// add our one-hit-wonder
|
||||||
|
cache.put(&[(&one_hit_wonder, executor.clone())]);
|
||||||
|
assert_eq!(cache.executors[&one_hit_wonder].hit_count.load(Relaxed), 1);
|
||||||
|
// displace the one-hit-wonder with "popular program"
|
||||||
|
cache.put(&[(&popular, executor.clone())]);
|
||||||
|
assert_eq!(cache.executors[&popular].hit_count.load(Relaxed), 1);
|
||||||
|
|
||||||
|
// one-hit-wonder counter incremented
|
||||||
|
assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 1);
|
||||||
|
|
||||||
|
// make "popular program" popular
|
||||||
|
cache.get(&popular).unwrap();
|
||||||
|
assert_eq!(cache.executors[&popular].hit_count.load(Relaxed), 2);
|
||||||
|
|
||||||
|
// evict "popular program"
|
||||||
|
cache.put(&[(&one_hit_wonder, executor.clone())]);
|
||||||
|
assert_eq!(cache.executors[&one_hit_wonder].hit_count.load(Relaxed), 1);
|
||||||
|
|
||||||
|
// one-hit-wonder counter not incremented
|
||||||
|
assert_eq!(cache.stats.one_hit_wonders.load(Relaxed), 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bank_executor_cache() {
|
fn test_bank_executor_cache() {
|
||||||
solana_logger::setup();
|
solana_logger::setup();
|
||||||
|
|
Loading…
Reference in New Issue