diff --git a/program-runtime/src/loaded_programs.rs b/program-runtime/src/loaded_programs.rs index b59320305d3571..e606aa536d8400 100644 --- a/program-runtime/src/loaded_programs.rs +++ b/program-runtime/src/loaded_programs.rs @@ -348,6 +348,8 @@ impl LoadedProgram { } } +type LoadedProgramList = Vec<(Pubkey, Arc)>; + #[derive(Debug, Default)] pub struct LoadedPrograms { /// A two level index: @@ -356,6 +358,8 @@ pub struct LoadedPrograms { entries: HashMap>>, /// Globally shared RBPF config and syscall registry pub program_runtime_environment_v1: ProgramRuntimeEnvironment, + pub upcoming_program_runtime_environment_v1: + Option<(ProgramRuntimeEnvironment, LoadedProgramList)>, latest_root: Slot, pub stats: Stats, } @@ -655,7 +659,7 @@ impl LoadedPrograms { }) } - pub fn get_entries_sorted_by_tx_usage(&self) -> Vec<(Pubkey, Arc)> { + pub fn get_entries_sorted_by_tx_usage(&self) -> LoadedProgramList { self.entries .iter() .flat_map(|(id, list)| { diff --git a/programs/bpf_loader/src/syscalls/mod.rs b/programs/bpf_loader/src/syscalls/mod.rs index 654d88114410b9..60db957d61877c 100644 --- a/programs/bpf_loader/src/syscalls/mod.rs +++ b/programs/bpf_loader/src/syscalls/mod.rs @@ -148,8 +148,6 @@ pub fn create_program_runtime_environment<'a>( debugging_features: bool, ) -> Result>, Error> { use rand::Rng; - // When adding new features for RBPF, - // also add them to `Bank::apply_builtin_program_feature_transitions()`. let config = Config { max_call_depth: compute_budget.max_call_depth, stack_frame_size: compute_budget.stack_frame_size, diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index d737798f1b46bd..d89fd7f175cb0a 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -1716,11 +1716,10 @@ impl Bank { }); // Following code may touch AccountsDb, requiring proper ancestors - let parent_epoch = parent.epoch(); let (_, update_epoch_time_us) = measure_us!({ - if parent_epoch < new.epoch() { + if parent.epoch() < new.epoch() { new.process_new_epoch( - parent_epoch, + parent.epoch(), parent.slot(), parent.block_height(), reward_calc_tracer, @@ -1729,6 +1728,47 @@ impl Bank { // Save a snapshot of stakes for use in consensus and stake weighted networking let leader_schedule_epoch = epoch_schedule.get_leader_schedule_epoch(slot); new.update_epoch_stakes(leader_schedule_epoch); + // Recompile loaded programs one at a time before the next epoch hits + let (_epoch, slot_index) = new.get_epoch_and_slot_index(new.slot()); + let mut loaded_programs_cache = new.loaded_programs_cache.write().unwrap(); + if let Some((_env, ref mut programs_to_recompile)) = + &mut loaded_programs_cache.upcoming_program_runtime_environment_v1 + { + + } else if slot_index.saturating_add( + solana_program_runtime::loaded_programs::MAX_LOADED_ENTRY_COUNT + .checked_div(2) + .unwrap() as u64, + ) >= new.get_slots_in_epoch(new.epoch()) + { + // Anticipate the upcoming program runtime environment for the next epoch, + // so we can try to recompile cached entries before the feature transition hits. + drop(loaded_programs_cache); + let (feature_set, _new_feature_activations) = + new.compute_active_feature_set(true); + let program_runtime_environment_v1 = create_program_runtime_environment( + &feature_set, + &new.runtime_config.compute_budget.unwrap_or_default(), + false, /* deployment */ + false, /* debugging_features */ + ) + .unwrap(); + let mut loaded_programs_cache = new.loaded_programs_cache.write().unwrap(); + loaded_programs_cache.upcoming_program_runtime_environment_v1 = + if *loaded_programs_cache.program_runtime_environment_v1 + == program_runtime_environment_v1 + { + Some(( + loaded_programs_cache.program_runtime_environment_v1.clone(), + Vec::new(), + )) + } else { + Some(( + Arc::new(program_runtime_environment_v1), + loaded_programs_cache.get_entries_sorted_by_tx_usage(), + )) + }; + } } if new.is_partitioned_rewards_code_enabled() { new.distribute_partitioned_epoch_rewards(); @@ -1738,8 +1778,8 @@ impl Bank { // Update sysvars before processing transactions let (_, update_sysvars_time_us) = measure_us!({ new.update_slot_hashes(); - new.update_stake_history(Some(parent_epoch)); - new.update_clock(Some(parent_epoch)); + new.update_stake_history(Some(parent.epoch())); + new.update_clock(Some(parent.epoch())); new.update_fees(); }); @@ -5156,12 +5196,16 @@ impl Bank { } else { None }; - let program_runtime_environment_v1 = self - .loaded_programs_cache - .read() - .unwrap() - .program_runtime_environment_v1 - .clone(); + let loaded_programs_cache = self.loaded_programs_cache.read().unwrap(); + let program_runtime_environment = if recompile.is_some() { + loaded_programs_cache + .upcoming_program_runtime_environment_v1 + .as_ref() + .map(|(env, _programs_to_recompile)| env) + .unwrap() + } else { + &loaded_programs_cache.program_runtime_environment_v1 + }; solana_bpf_loader_program::load_program_from_account( &self.feature_set, None, // log_collector @@ -8525,18 +8569,12 @@ impl Bank { only_apply_transitions_for_new_features: bool, new_feature_activations: &HashSet, ) { - const FEATURES_AFFECTING_RBPF: &[Pubkey] = &[ - feature_set::error_on_syscall_bpf_function_hash_collisions::id(), - feature_set::reject_callx_r10::id(), - feature_set::switch_to_new_elf_parser::id(), - feature_set::bpf_account_data_direct_mapping::id(), - feature_set::enable_alt_bn128_syscall::id(), - ]; - if !only_apply_transitions_for_new_features - || FEATURES_AFFECTING_RBPF - .iter() - .any(|key| new_feature_activations.contains(key)) { + let mut loaded_programs_cache = self.loaded_programs_cache.write().unwrap(); + let upcoming_program_runtime_environment_v1 = loaded_programs_cache + .upcoming_program_runtime_environment_v1 + .take() + .map(|(env, _programs_to_recompile)| env); let program_runtime_environment_v1 = create_program_runtime_environment( &self.feature_set, &self.runtime_config.compute_budget.unwrap_or_default(), @@ -8544,12 +8582,17 @@ impl Bank { false, /* debugging_features */ ) .unwrap(); - let mut loaded_programs_cache = self.loaded_programs_cache.write().unwrap(); if *loaded_programs_cache.program_runtime_environment_v1 != program_runtime_environment_v1 { - loaded_programs_cache.program_runtime_environment_v1 = - Arc::new(program_runtime_environment_v1); + loaded_programs_cache.program_runtime_environment_v1 = if let Some(upcoming) = + upcoming_program_runtime_environment_v1 + .filter(|env| &program_runtime_environment_v1 == env.as_ref()) + { + upcoming + } else { + Arc::new(program_runtime_environment_v1) + }; } loaded_programs_cache.prune_feature_set_transition(); }