diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 3b19342f7cfc67..f54829b69837c0 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -638,6 +638,8 @@ pub struct ProgramCacheForTxBatch { /// The epoch of the last rerooting pub latest_root_epoch: Epoch, pub hit_max_limit: bool, + pub loaded_missing: bool, + pub merged_modified: bool, } impl ProgramCacheForTxBatch { @@ -654,6 +656,8 @@ impl ProgramCacheForTxBatch { upcoming_environments, latest_root_epoch, hit_max_limit: false, + loaded_missing: false, + merged_modified: false, } } @@ -669,6 +673,8 @@ impl ProgramCacheForTxBatch { upcoming_environments: cache.get_upcoming_environments_for_epoch(epoch), latest_root_epoch: cache.latest_root_epoch, hit_max_limit: false, + loaded_missing: false, + merged_modified: false, } } @@ -722,9 +728,14 @@ impl ProgramCacheForTxBatch { pub fn merge(&mut self, other: &Self) { other.entries.iter().for_each(|(key, entry)| { + self.merged_modified = true; self.replenish(*key, entry.clone()); }) } + + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } } pub enum ProgramCacheMatchCriteria { diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index de7dcf78b4c625..17ee43f41cc83e 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -4156,18 +4156,23 @@ impl Bank { let mut store_executors_which_were_deployed_time = Measure::start("store_executors_which_were_deployed_time"); + let mut cache = None; for execution_result in &execution_results { if let TransactionExecutionResult::Executed { details, programs_modified_by_tx, } = execution_result { - if details.status.is_ok() { - let mut cache = self.transaction_processor.program_cache.write().unwrap(); - cache.merge(programs_modified_by_tx); + if details.status.is_ok() && !programs_modified_by_tx.is_empty() { + cache + .get_or_insert_with(|| { + self.transaction_processor.program_cache.write().unwrap() + }) + .merge(programs_modified_by_tx); } } } + drop(cache); store_executors_which_were_deployed_time.stop(); saturating_add_assign!( timings.execute_accessories.update_executors_us, diff --git a/svm/src/transaction_processor.rs b/svm/src/transaction_processor.rs index 9b7cc4b6956bbc..4fb54e5a1f1713 100644 --- a/svm/src/transaction_processor.rs +++ b/svm/src/transaction_processor.rs @@ -292,14 +292,22 @@ impl TransactionBatchProcessor { execution_time.stop(); - const SHRINK_LOADED_PROGRAMS_TO_PERCENTAGE: u8 = 90; - self.program_cache - .write() - .unwrap() - .evict_using_2s_random_selection( - Percentage::from(SHRINK_LOADED_PROGRAMS_TO_PERCENTAGE), - self.slot, - ); + // Skip eviction when there's no chance this particular tx batch has increased the size of + // ProgramCache entries. Note that loaded_missing is deliberately defined, so that there's + // still at least one other batch, which will evict the program cache, even after the + // occurrences of cooperative loading. + if programs_loaded_for_tx_batch.borrow().loaded_missing + || programs_loaded_for_tx_batch.borrow().merged_modified + { + const SHRINK_LOADED_PROGRAMS_TO_PERCENTAGE: u8 = 90; + self.program_cache + .write() + .unwrap() + .evict_using_2s_random_selection( + Percentage::from(SHRINK_LOADED_PROGRAMS_TO_PERCENTAGE), + self.slot, + ); + } debug!( "load: {}us execute: {}us txs_len={}", @@ -395,6 +403,7 @@ impl TransactionBatchProcessor { } // Submit our last completed loading task. if let Some((key, program)) = program_to_store.take() { + loaded_programs_for_txs.as_mut().unwrap().loaded_missing = true; if program_cache.finish_cooperative_loading_task(self.slot, key, program) && limit_to_load_programs {