diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index 14e6073793be07..d1a98b9149bc44 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -227,7 +227,7 @@ pub struct ProgramCacheStats { pub prunes_orphan: AtomicU64, /// a program got pruned because it was not recompiled for the next epoch pub prunes_environment: AtomicU64, - /// the [SecondLevel] was empty because all slot versions got pruned + /// a program had no entries because all slot versions got pruned pub empty_entries: AtomicU64, } @@ -578,13 +578,6 @@ impl LoadingTaskWaiter { } } -/// Contains all the program versions at a specific address. -#[derive(Debug, Default)] -struct SecondLevel { - /// List of all versions (across all forks) of a program sorted by the slot in which they were modified - slot_versions: Vec>, -} - /// This structure is the global cache of loaded, verified and compiled programs. /// /// It ... @@ -603,8 +596,9 @@ struct SecondLevel { pub struct ProgramCache { /// A two level index: /// - /// The first level is for the address at which programs are deployed and the second level for the slot (and thus also fork). - entries: HashMap, + /// - the first level is for the address at which programs are deployed + /// - the second level for the slot (and thus also fork), sorted by slot number. + entries: HashMap>>, /// The entries that are getting loaded and have not yet finished loading. /// /// The key is the program address, the value is a tuple of the slot in which the program is @@ -824,7 +818,7 @@ impl ProgramCache { &entry.program, ProgramCacheEntryType::DelayVisibility )); - let slot_versions = &mut self.entries.entry(key).or_default().slot_versions; + let slot_versions = &mut self.entries.entry(key).or_default(); match slot_versions.binary_search_by(|at| { at.effective_slot .cmp(&entry.effective_slot) @@ -865,9 +859,7 @@ impl ProgramCache { pub fn prune_by_deployment_slot(&mut self, slot: Slot) { for second_level in self.entries.values_mut() { - second_level - .slot_versions - .retain(|entry| entry.deployment_slot != slot); + second_level.retain(|entry| entry.deployment_slot != slot); } self.remove_programs_with_no_entries(); } @@ -895,8 +887,7 @@ impl ProgramCache { // Remove entries un/re/deployed on orphan forks let mut first_ancestor_found = false; let mut first_ancestor_env = None; - second_level.slot_versions = second_level - .slot_versions + *second_level = second_level .iter() .rev() .filter(|entry| { @@ -945,7 +936,7 @@ impl ProgramCache { }) .cloned() .collect(); - second_level.slot_versions.reverse(); + second_level.reverse(); } self.remove_programs_with_no_entries(); debug_assert!(self.latest_root_slot <= new_root_slot); @@ -989,7 +980,7 @@ impl ProgramCache { let mut cooperative_loading_task = None; search_for.retain(|(key, (match_criteria, usage_count))| { if let Some(second_level) = self.entries.get(key) { - for entry in second_level.slot_versions.iter().rev() { + for entry in second_level.iter().rev() { if entry.deployment_slot <= self.latest_root_slot || matches!( locked_fork_graph.relationship( @@ -1107,7 +1098,6 @@ impl ProgramCache { .iter() .flat_map(|(id, second_level)| { second_level - .slot_versions .iter() .filter_map(move |program| match program.program { ProgramCacheEntryType::Loaded(_) => { @@ -1132,19 +1122,16 @@ impl ProgramCache { self.entries .iter() .flat_map(|(id, second_level)| { - second_level - .slot_versions - .iter() - .map(|program| (*id, program.clone())) + second_level.iter().map(|program| (*id, program.clone())) }) .collect() } - /// Returns the `slot_versions` of the second level for the given program id. + /// Returns the slot versions for the given program id. pub fn get_slot_versions_for_tests(&self, key: &Pubkey) -> &[Arc] { self.entries .get(key) - .map(|second_level| second_level.slot_versions.as_ref()) + .map(|second_level| second_level.as_ref()) .unwrap_or(&[]) } @@ -1205,7 +1192,6 @@ impl ProgramCache { fn unload_program_entry(&mut self, program: &Pubkey, remove_entry: &Arc) { let second_level = self.entries.get_mut(program).expect("Cache lookup failed"); let candidate = second_level - .slot_versions .iter_mut() .find(|entry| entry == &remove_entry) .expect("Program entry not found"); @@ -1238,7 +1224,7 @@ impl ProgramCache { fn remove_programs_with_no_entries(&mut self) { let num_programs_before_removal = self.entries.len(); self.entries - .retain(|_key, second_level| !second_level.slot_versions.is_empty()); + .retain(|_key, second_level| !second_level.is_empty()); if self.entries.len() < num_programs_before_removal { self.stats.empty_entries.fetch_add( num_programs_before_removal.saturating_sub(self.entries.len()) as u64, @@ -2070,7 +2056,7 @@ mod tests { keys.iter() .filter_map(|key| { let visible_entry = cache.entries.get(key).and_then(|second_level| { - second_level.slot_versions.iter().rev().find(|entry| { + second_level.iter().rev().find(|entry| { matches!( locked_fork_graph.relationship(entry.deployment_slot, loading_slot), BlockRelation::Equal | BlockRelation::Ancestor,