Replace executor cache usage with LoadedPrograms cache (#31462)

* Replace executor cache usage with LoadedPrograms cache

* clippy fixes

* update cache with updated programs

* fixes

* more cleanup

* update tx batch cache with the tx results

* address review comments

* handle program closing backward compatibility

* handle unloaded programs during extraction
This commit is contained in:
Pankaj Garg 2023-05-09 15:44:38 -07:00 committed by GitHub
parent fb7ba97afc
commit 2210af60ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 233 additions and 129 deletions

View File

@ -15,7 +15,9 @@ use {
},
solana_program_runtime::{
invoke_context::InvokeContext,
loaded_programs::{LoadProgramMetrics, LoadedProgram, LoadedProgramType},
loaded_programs::{
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
},
with_mock_invoke_context,
},
solana_rbpf::{
@ -416,6 +418,9 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
bank.get_builtin_programs()
);
// Adding `DELAY_VISIBILITY_SLOT_OFFSET` to slots to accommodate for delay visibility of the program
let mut loaded_programs =
LoadedProgramsForTxBatch::new(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
for key in cached_account_keys {
let program = bank.load_program(&key, true).unwrap_or_else(|err| {
// Create a tombstone for the program in the cache
@ -426,11 +431,9 @@ pub fn run(ledger_path: &Path, matches: &ArgMatches<'_>) {
))
});
debug!("Loaded program {}", key);
invoke_context
.tx_executor_cache
.borrow_mut()
.set(key, program, false, false, 0);
loaded_programs.replenish(key, program);
}
invoke_context.programs_loaded_for_tx_batch = Rc::new(RefCell::new(loaded_programs));
invoke_context
.transaction_context

View File

@ -3,9 +3,8 @@ use {
accounts_data_meter::AccountsDataMeter,
builtin_program::{BuiltinPrograms, ProcessInstructionWithContext},
compute_budget::ComputeBudget,
executor_cache::TransactionExecutorCache,
ic_logger_msg, ic_msg,
loaded_programs::LoadedProgramType,
loaded_programs::{LoadedProgramType, LoadedProgramsForTxBatch},
log_collector::LogCollector,
pre_account::PreAccount,
stable_log,
@ -160,7 +159,9 @@ pub struct InvokeContext<'a> {
current_compute_budget: ComputeBudget,
compute_meter: RefCell<u64>,
accounts_data_meter: AccountsDataMeter,
pub tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
pub programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
pub programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
pub programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
pub feature_set: Arc<FeatureSet>,
pub timings: ExecuteDetailsTimings,
pub blockhash: Hash,
@ -178,7 +179,9 @@ impl<'a> InvokeContext<'a> {
sysvar_cache: &'a SysvarCache,
log_collector: Option<Rc<RefCell<LogCollector>>>,
compute_budget: ComputeBudget,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
feature_set: Arc<FeatureSet>,
blockhash: Hash,
lamports_per_signature: u64,
@ -195,7 +198,9 @@ impl<'a> InvokeContext<'a> {
compute_budget,
compute_meter: RefCell::new(compute_budget.compute_unit_limit),
accounts_data_meter: AccountsDataMeter::new(prev_accounts_data_len),
tx_executor_cache,
programs_loaded_for_tx_batch,
programs_modified_by_tx,
programs_updated_only_for_global_cache,
feature_set,
timings: ExecuteDetailsTimings::default(),
blockhash,
@ -902,8 +907,8 @@ macro_rules! with_mock_invoke_context_and_builtin_programs {
},
std::{cell::RefCell, rc::Rc, sync::Arc},
$crate::{
compute_budget::ComputeBudget, executor_cache::TransactionExecutorCache,
invoke_context::InvokeContext, log_collector::LogCollector,
compute_budget::ComputeBudget, invoke_context::InvokeContext,
loaded_programs::LoadedProgramsForTxBatch, log_collector::LogCollector,
sysvar_cache::SysvarCache,
},
};
@ -940,7 +945,9 @@ macro_rules! with_mock_invoke_context_and_builtin_programs {
&sysvar_cache,
Some(LogCollector::new_ref()),
compute_budget,
Rc::new(RefCell::new(TransactionExecutorCache::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
Hash::default(),
0,

View File

@ -281,9 +281,16 @@ pub struct LoadedProgramsForTxBatch {
}
impl LoadedProgramsForTxBatch {
pub fn new(slot: Slot) -> Self {
Self {
entries: HashMap::new(),
slot,
}
}
/// Refill the cache with a single entry. It's typically called during transaction loading, and
/// transaction processing (for program management instructions).
/// The replaces the existing entry (if any) with the provided entry. The return value contains
/// It replaces the existing entry (if any) with the provided entry. The return value contains
/// `true` if an entry existed.
/// The function also returns the newly inserted value.
pub fn replenish(
@ -291,7 +298,6 @@ impl LoadedProgramsForTxBatch {
key: Pubkey,
entry: Arc<LoadedProgram>,
) -> (bool, Arc<LoadedProgram>) {
debug_assert!(entry.effective_slot <= self.slot);
(self.entries.insert(key, entry.clone()).is_some(), entry)
}
@ -314,6 +320,16 @@ impl LoadedProgramsForTxBatch {
pub fn slot(&self) -> Slot {
self.slot
}
pub fn set_slot_for_tests(&mut self, slot: Slot) {
self.slot = slot;
}
pub fn merge(&mut self, other: &Self) {
other.entries.iter().for_each(|(key, entry)| {
self.replenish(*key, entry.clone());
})
}
}
pub enum LoadedProgramMatchCriteria {
@ -453,6 +469,12 @@ impl LoadedPrograms {
return None;
}
if matches!(entry.program, LoadedProgramType::Unloaded) {
// The program was unloaded. Consider it as missing, so it can be reloaded.
missing.push(key);
return None;
}
if current_slot >= entry.effective_slot {
return Some((key, entry.clone()));
} else if entry.is_implicit_delay_visibility_tombstone(current_slot) {
@ -483,6 +505,12 @@ impl LoadedPrograms {
)
}
pub fn merge(&mut self, tx_batch_cache: &LoadedProgramsForTxBatch) {
tx_batch_cache.entries.iter().for_each(|(key, entry)| {
self.replenish(*key, entry.clone());
})
}
/// Unloads programs which were used infrequently
pub fn sort_and_unload(&mut self, shrink_to: PercentageInteger) {
let sorted_candidates: Vec<(Pubkey, Arc<LoadedProgram>)> = self

View File

@ -10,7 +10,9 @@ use {
compute_budget::ComputeBudget,
ic_logger_msg, ic_msg,
invoke_context::{BpfAllocator, InvokeContext, SyscallContext},
loaded_programs::{LoadProgramMetrics, LoadedProgram, LoadedProgramType},
loaded_programs::{
LoadProgramMetrics, LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET,
},
log_collector::LogCollector,
stable_log,
sysvar_cache::get_sysvar_with_account_check,
@ -91,7 +93,7 @@ pub fn load_program_from_bytes(
register_syscalls_time.stop();
load_program_metrics.register_syscalls_us = register_syscalls_time.as_us();
let effective_slot = if feature_set.is_active(&delay_visibility_of_program_deployment::id()) {
deployment_slot.saturating_add(1)
deployment_slot.saturating_add(DELAY_VISIBILITY_SLOT_OFFSET)
} else {
deployment_slot
};
@ -190,12 +192,27 @@ pub fn load_program_from_account(
Ok((loaded_program, Some(load_program_metrics)))
}
fn find_program_in_cache(
invoke_context: &InvokeContext,
pubkey: &Pubkey,
) -> Option<Arc<LoadedProgram>> {
// First lookup the cache of the programs modified by the current transaction. If not found, lookup
// the cache of the cache of the programs that are loaded for the transaction batch.
invoke_context
.programs_modified_by_tx
.borrow()
.find(pubkey)
.or_else(|| {
invoke_context
.programs_loaded_for_tx_batch
.borrow()
.find(pubkey)
})
}
macro_rules! deploy_program {
($invoke_context:expr, $program_id:expr, $loader_key:expr,
$account_size:expr, $slot:expr, $drop:expr, $new_programdata:expr $(,)?) => {{
let delay_visibility_of_program_deployment = $invoke_context
.feature_set
.is_active(&delay_visibility_of_program_deployment::id());
let mut load_program_metrics = LoadProgramMetrics::default();
let executor = load_program_from_bytes(
&$invoke_context.feature_set,
@ -209,20 +226,14 @@ macro_rules! deploy_program {
true, /* reject_deployment_of_broken_elfs */
false, /* debugging_features */
)?;
if let Some(old_entry) = $invoke_context.tx_executor_cache.borrow().get(&$program_id) {
if let Some(old_entry) = find_program_in_cache($invoke_context, &$program_id) {
let usage_counter = old_entry.usage_counter.load(Ordering::Relaxed);
executor.usage_counter.store(usage_counter, Ordering::Relaxed);
}
$drop
load_program_metrics.program_id = $program_id.to_string();
load_program_metrics.submit_datapoint(&mut $invoke_context.timings);
$invoke_context.tx_executor_cache.borrow_mut().set(
$program_id,
Arc::new(executor),
true,
delay_visibility_of_program_deployment,
$slot,
);
$invoke_context.programs_modified_by_tx.borrow_mut().replenish($program_id, Arc::new(executor));
}};
}
@ -562,10 +573,7 @@ fn process_instruction_inner(
}
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
let executor = invoke_context
.tx_executor_cache
.borrow()
.get(program_account.get_key())
let executor = find_program_in_cache(invoke_context, program_account.get_key())
.ok_or(InstructionError::InvalidAccountData)?;
if executor.is_tombstone() {
@ -1265,15 +1273,32 @@ fn process_loader_upgradeable_instruction(
instruction_context,
&log_collector,
)?;
let clock = invoke_context.get_sysvar_cache().get_clock()?;
if invoke_context
.feature_set
.is_active(&delay_visibility_of_program_deployment::id())
{
let clock = invoke_context.get_sysvar_cache().get_clock()?;
invoke_context
.tx_executor_cache
.programs_modified_by_tx
.borrow_mut()
.set_tombstone(program_key, clock.slot);
.replenish(
program_key,
Arc::new(LoadedProgram::new_tombstone(
clock.slot,
LoadedProgramType::Closed,
)),
);
} else {
invoke_context
.programs_updated_only_for_global_cache
.borrow_mut()
.replenish(
program_key,
Arc::new(LoadedProgram::new_tombstone(
clock.slot,
LoadedProgramType::Closed,
)),
);
}
}
_ => {
@ -1661,7 +1686,10 @@ fn execute<'a, 'b: 'a>(
}
pub mod test_utils {
use {super::*, solana_sdk::account::ReadableAccount};
use {
super::*, solana_program_runtime::loaded_programs::DELAY_VISIBILITY_SLOT_OFFSET,
solana_sdk::account::ReadableAccount,
};
pub fn load_all_invoked_programs(invoke_context: &mut InvokeContext) {
let num_accounts = invoke_context.transaction_context.get_number_of_accounts();
@ -1693,8 +1721,9 @@ pub mod test_utils {
true,
false,
) {
let mut cache = invoke_context.tx_executor_cache.borrow_mut();
cache.set(*pubkey, Arc::new(loaded_program), true, false, 0)
let mut cache = invoke_context.programs_modified_by_tx.borrow_mut();
cache.set_slot_for_tests(DELAY_VISIBILITY_SLOT_OFFSET);
cache.replenish(*pubkey, Arc::new(loaded_program));
}
}
}
@ -4096,13 +4125,10 @@ mod tests {
maybe_expiration_slot: None,
usage_counter: AtomicU64::new(100),
};
invoke_context.tx_executor_cache.borrow_mut().set(
program_id,
Arc::new(program),
false,
false,
0,
);
invoke_context
.programs_modified_by_tx
.borrow_mut()
.replenish(program_id, Arc::new(program));
assert!(matches!(
deploy_test_program(&mut invoke_context, program_id,),
@ -4110,9 +4136,9 @@ mod tests {
));
let updated_program = invoke_context
.tx_executor_cache
.programs_modified_by_tx
.borrow()
.get(&program_id)
.find(&program_id)
.expect("Didn't find upgraded program in the cache");
assert_eq!(updated_program.deployment_slot, 2);
@ -4132,13 +4158,10 @@ mod tests {
maybe_expiration_slot: None,
usage_counter: AtomicU64::new(100),
};
invoke_context.tx_executor_cache.borrow_mut().set(
program_id,
Arc::new(program),
false,
false,
0,
);
invoke_context
.programs_modified_by_tx
.borrow_mut()
.replenish(program_id, Arc::new(program));
let program_id2 = Pubkey::new_unique();
assert!(matches!(
@ -4147,9 +4170,9 @@ mod tests {
));
let program2 = invoke_context
.tx_executor_cache
.programs_modified_by_tx
.borrow()
.get(&program_id2)
.find(&program_id2)
.expect("Didn't find upgraded program in the cache");
assert_eq!(program2.deployment_slot, 2);

View File

@ -379,7 +379,10 @@ fn test_program_sbf_loader_deprecated() {
let bank = Bank::new_for_tests(&genesis_config);
let program_id = create_program(&bank, &bpf_loader_deprecated::id(), program);
let bank_client = BankClient::new(bank);
let mut bank_client = BankClient::new(bank);
bank_client
.advance_slot(1, &Pubkey::default())
.expect("Failed to advance the slot");
let account_metas = vec![AccountMeta::new(mint_keypair.pubkey(), true)];
let instruction = Instruction::new_with_bytes(program_id, &[1], account_metas);
let result = bank_client.send_and_confirm_instruction(&mint_keypair, instruction);
@ -2065,8 +2068,10 @@ fn test_program_sbf_invoke_in_same_tx_as_redeployment() {
],
);
// load_upgradeable_program sets clock sysvar to 1, which causes the program to be effective
// after 2 slots. So we need to advance the bank client by 2 slots here.
let bank = bank_client
.advance_slot(1, &Pubkey::default())
.advance_slot(2, &Pubkey::default())
.expect("Failed to advance slot");
// Prepare redeployment
@ -2160,8 +2165,10 @@ fn test_program_sbf_invoke_in_same_tx_as_undeployment() {
],
);
// load_upgradeable_program sets clock sysvar to 1, which causes the program to be effective
// after 2 slots. So we need to advance the bank client by 2 slots here.
let bank = bank_client
.advance_slot(1, &Pubkey::default())
.advance_slot(2, &Pubkey::default())
.expect("Failed to advance slot");
// Prepare undeployment
@ -3904,7 +3911,10 @@ fn test_program_sbf_inner_instruction_alignment_checks() {
// invoke unaligned program, which will call aligned program twice,
// unaligned should be allowed once invoke completes
let bank_client = BankClient::new(bank);
let mut bank_client = BankClient::new(bank);
bank_client
.advance_slot(1, &Pubkey::default())
.expect("Failed to advance the slot");
let mut instruction = Instruction::new_with_bytes(
inner_instruction_alignment_check,
&[0],

View File

@ -26,7 +26,7 @@ use {
solana_address_lookup_table_program::{error::AddressLookupError, state::AddressLookupTable},
solana_program_runtime::{
compute_budget::{self, ComputeBudget},
loaded_programs::{LoadedProgram, LoadedProgramType},
loaded_programs::{LoadedProgram, LoadedProgramType, LoadedProgramsForTxBatch},
},
solana_sdk::{
account::{Account, AccountSharedData, ReadableAccount, WritableAccount},
@ -337,7 +337,7 @@ impl Accounts {
feature_set: &FeatureSet,
account_overrides: Option<&AccountOverrides>,
program_accounts: &HashMap<Pubkey, &Pubkey>,
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>,
loaded_programs: &LoadedProgramsForTxBatch,
) -> Result<LoadedTransaction> {
// NOTE: this check will never fail because `tx` is sanitized
if tx.signatures().is_empty() && fee != 0 {
@ -390,7 +390,7 @@ impl Accounts {
(account_override.data().len(), account_override.clone(), 0)
} else if let Some(program) = (!instruction_account && !message.is_writable(i))
.then_some(())
.and_then(|_| loaded_programs.get(key))
.and_then(|_| loaded_programs.find(key))
{
// This condition block does special handling for accounts that are passed
// as instruction account to any of the instructions in the transaction.
@ -401,7 +401,7 @@ impl Accounts {
Self::account_shared_data_from_program(
key,
feature_set,
program,
program.as_ref(),
program_accounts,
)
.map(|program_account| (program.account_size, program_account, 0))?
@ -695,7 +695,7 @@ impl Accounts {
fee_structure: &FeeStructure,
account_overrides: Option<&AccountOverrides>,
program_accounts: &HashMap<Pubkey, &Pubkey>,
loaded_programs: &HashMap<Pubkey, Arc<LoadedProgram>>,
loaded_programs: &LoadedProgramsForTxBatch,
) -> Vec<TransactionLoadResult> {
txs.iter()
.zip(lock_results)
@ -1470,9 +1470,8 @@ mod tests {
},
assert_matches::assert_matches,
solana_address_lookup_table_program::state::LookupTableMeta,
solana_program_runtime::{
executor_cache::TransactionExecutorCache,
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
solana_program_runtime::prioritization_fee::{
PrioritizationFeeDetails, PrioritizationFeeType,
},
solana_sdk::{
account::{AccountSharedData, WritableAccount},
@ -1525,7 +1524,10 @@ mod tests {
executed_units: 0,
accounts_data_len_delta: 0,
},
tx_executor_cache: Rc::new(RefCell::new(TransactionExecutorCache::default())),
programs_modified_by_tx: Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
programs_updated_only_for_global_cache: Rc::new(RefCell::new(
LoadedProgramsForTxBatch::default(),
)),
}
}
@ -1563,7 +1565,7 @@ mod tests {
fee_structure,
None,
&HashMap::new(),
&HashMap::new(),
&LoadedProgramsForTxBatch::default(),
)
}
@ -3367,7 +3369,7 @@ mod tests {
&FeeStructure::default(),
account_overrides,
&HashMap::new(),
&HashMap::new(),
&LoadedProgramsForTxBatch::default(),
)
}

View File

@ -33,6 +33,8 @@
//! It offers a high-level API that signs transactions
//! on behalf of the caller, and a low-level API for when they have
//! already been signed and verified.
#[cfg(test)]
use solana_program_runtime::executor_cache::TransactionExecutorCache;
#[allow(deprecated)]
use solana_sdk::recent_blockhashes_account;
pub use solana_sdk::reward_type::RewardType;
@ -95,7 +97,7 @@ use {
accounts_data_meter::MAX_ACCOUNTS_DATA_LEN,
builtin_program::BuiltinPrograms,
compute_budget::{self, ComputeBudget},
executor_cache::{BankExecutorCache, TransactionExecutorCache, MAX_CACHED_EXECUTORS},
executor_cache::{BankExecutorCache, MAX_CACHED_EXECUTORS},
loaded_programs::{
LoadedProgram, LoadedProgramMatchCriteria, LoadedProgramType, LoadedPrograms,
LoadedProgramsForTxBatch, WorkingSlot,
@ -321,7 +323,8 @@ pub struct TransactionExecutionDetails {
pub enum TransactionExecutionResult {
Executed {
details: TransactionExecutionDetails,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
},
NotExecuted(TransactionError),
}
@ -4108,6 +4111,7 @@ impl Bank {
}
/// Add executors back to the bank's cache if they were missing and not re-/deployed
#[cfg(test)]
fn store_executors_which_added_to_the_cache(
&self,
tx_executor_cache: &RefCell<TransactionExecutorCache>,
@ -4124,6 +4128,7 @@ impl Bank {
}
/// Add re-/deployed executors to the bank's cache
#[cfg(test)]
fn store_executors_which_were_deployed(
&self,
tx_executor_cache: &RefCell<TransactionExecutorCache>,
@ -4174,7 +4179,6 @@ impl Bank {
}
}
#[allow(dead_code)] // Preparation for BankExecutorCache rework
pub fn load_program(
&self,
pubkey: &Pubkey,
@ -4263,7 +4267,7 @@ impl Bank {
timings: &mut ExecuteTimings,
error_counters: &mut TransactionErrorMetrics,
log_messages_bytes_limit: Option<usize>,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
) -> TransactionExecutionResult {
let prev_accounts_data_len = self.load_accounts_data_size();
let transaction_accounts = std::mem::take(&mut loaded_transaction.accounts);
@ -4313,7 +4317,10 @@ impl Bank {
let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature();
let mut executed_units = 0u64;
let programs_modified_by_tx =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::new(self.slot)));
let programs_updated_only_for_global_cache =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::new(self.slot)));
let mut process_message_time = Measure::start("process_message_time");
let process_result = MessageProcessor::process_message(
&self.builtin_programs,
@ -4322,7 +4329,9 @@ impl Bank {
&mut transaction_context,
self.rent_collector.rent,
log_collector.clone(),
tx_executor_cache.clone(),
programs_loaded_for_tx_batch,
programs_modified_by_tx.clone(),
programs_updated_only_for_global_cache.clone(),
self.feature_set.clone(),
compute_budget,
timings,
@ -4339,15 +4348,6 @@ impl Bank {
process_message_time.as_us()
);
let mut store_executors_which_added_to_the_cache_time =
Measure::start("store_executors_which_added_to_the_cache_time");
self.store_executors_which_added_to_the_cache(&tx_executor_cache);
store_executors_which_added_to_the_cache_time.stop();
saturating_add_assign!(
timings.execute_accessories.update_executors_us,
store_executors_which_added_to_the_cache_time.as_us()
);
let status = process_result
.and_then(|info| {
let post_account_state_info =
@ -4435,7 +4435,8 @@ impl Bank {
executed_units,
accounts_data_len_delta,
},
tx_executor_cache,
programs_modified_by_tx,
programs_updated_only_for_global_cache,
}
}
@ -4448,8 +4449,7 @@ impl Bank {
|| self.cluster_type() != ClusterType::MainnetBeta
}
#[allow(dead_code)] // Preparation for BankExecutorCache rework
fn load_and_get_programs_from_cache(
fn replenish_program_cache(
&self,
program_accounts_map: &HashMap<Pubkey, &Pubkey>,
) -> LoadedProgramsForTxBatch {
@ -4507,6 +4507,7 @@ impl Bank {
loaded_programs_for_txs
}
#[allow(dead_code)] // Preparation for BankExecutorCache rework
fn replenish_executor_cache(
&self,
program_accounts_map: &HashMap<Pubkey, &Pubkey>,
@ -4626,17 +4627,9 @@ impl Bank {
&self.blockhash_queue.read().unwrap(),
);
// The following code is currently commented out. This is how the new cache will
// finally be used, once rest of the code blocks are in place.
/*
let loaded_programs_map =
self.load_and_get_programs_from_cache(&program_accounts_map);
*/
let loaded_programs_map = self.replenish_executor_cache(&program_accounts_map);
let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::new(
loaded_programs_map.clone().into_iter(),
)));
let programs_loaded_for_tx_batch = Rc::new(RefCell::new(
self.replenish_program_cache(&program_accounts_map),
));
let mut load_time = Measure::start("accounts_load");
let mut loaded_transactions = self.rc.accounts.load_accounts(
@ -4650,7 +4643,7 @@ impl Bank {
&self.fee_structure,
account_overrides,
&program_accounts_map,
&loaded_programs_map,
&programs_loaded_for_tx_batch.borrow(),
);
load_time.stop();
@ -4696,7 +4689,7 @@ impl Bank {
compute_budget
};
self.execute_loaded_transaction(
let result = self.execute_loaded_transaction(
tx,
loaded_transaction,
compute_budget,
@ -4707,8 +4700,25 @@ impl Bank {
timings,
&mut error_counters,
log_messages_bytes_limit,
tx_executor_cache.clone(),
)
programs_loaded_for_tx_batch.clone(),
);
if let TransactionExecutionResult::Executed {
details,
programs_modified_by_tx,
programs_updated_only_for_global_cache: _,
} = &result
{
// Update batch specific cache of the loaded programs with the modifications
// made by the transaction, if it executed successfully.
if details.status.is_ok() {
programs_loaded_for_tx_batch
.borrow_mut()
.merge(&programs_modified_by_tx.borrow());
}
}
result
}
})
.collect();
@ -5193,11 +5203,14 @@ impl Bank {
for execution_result in &execution_results {
if let TransactionExecutionResult::Executed {
details,
tx_executor_cache,
programs_modified_by_tx,
programs_updated_only_for_global_cache,
} = execution_result
{
if details.status.is_ok() {
self.store_executors_which_were_deployed(tx_executor_cache);
let mut cache = self.loaded_programs_cache.write().unwrap();
cache.merge(&programs_modified_by_tx.borrow());
cache.merge(&programs_updated_only_for_global_cache.borrow());
}
}
}

View File

@ -38,7 +38,7 @@ use {
declare_process_instruction,
executor_cache::TransactionExecutorCache,
invoke_context::mock_process_instruction,
loaded_programs::{LoadedProgram, LoadedProgramType},
loaded_programs::{LoadedProgram, LoadedProgramType, DELAY_VISIBILITY_SLOT_OFFSET},
prioritization_fee::{PrioritizationFeeDetails, PrioritizationFeeType},
timings::ExecuteTimings,
},
@ -176,7 +176,10 @@ fn new_execution_result(
executed_units: 0,
accounts_data_len_delta: 0,
},
tx_executor_cache: Rc::new(RefCell::new(TransactionExecutorCache::default())),
programs_modified_by_tx: Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
programs_updated_only_for_global_cache: Rc::new(RefCell::new(
LoadedProgramsForTxBatch::default(),
)),
}
}
@ -7771,14 +7774,9 @@ fn test_bpf_loader_upgradeable_deploy_with_max_len() {
Ok(()),
solana_bpf_loader_program::process_instruction,
|invoke_context| {
let mut cache = invoke_context.tx_executor_cache.borrow_mut();
cache.set(
program_keypair.pubkey(),
loaded_program.clone(),
true,
false,
0,
);
let mut cache = invoke_context.programs_modified_by_tx.borrow_mut();
cache.set_slot_for_tests(bank.slot() + DELAY_VISIBILITY_SLOT_OFFSET);
cache.replenish(program_keypair.pubkey(), loaded_program.clone());
},
|_invoke_context| {},
);
@ -11506,7 +11504,7 @@ fn test_rent_state_list_len() {
&FeeStructure::default(),
None,
&HashMap::new(),
&HashMap::new(),
&LoadedProgramsForTxBatch::default(),
);
let compute_budget = bank.runtime_config.compute_budget.unwrap_or_else(|| {

View File

@ -4,8 +4,8 @@ use {
solana_program_runtime::{
builtin_program::BuiltinPrograms,
compute_budget::ComputeBudget,
executor_cache::TransactionExecutorCache,
invoke_context::InvokeContext,
loaded_programs::LoadedProgramsForTxBatch,
log_collector::LogCollector,
sysvar_cache::SysvarCache,
timings::{ExecuteDetailsTimings, ExecuteTimings},
@ -58,7 +58,9 @@ impl MessageProcessor {
transaction_context: &mut TransactionContext,
rent: Rent,
log_collector: Option<Rc<RefCell<LogCollector>>>,
tx_executor_cache: Rc<RefCell<TransactionExecutorCache>>,
programs_loaded_for_tx_batch: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_modified_by_tx: Rc<RefCell<LoadedProgramsForTxBatch>>,
programs_updated_only_for_global_cache: Rc<RefCell<LoadedProgramsForTxBatch>>,
feature_set: Arc<FeatureSet>,
compute_budget: ComputeBudget,
timings: &mut ExecuteTimings,
@ -75,7 +77,9 @@ impl MessageProcessor {
sysvar_cache,
log_collector,
compute_budget,
tx_executor_cache,
programs_loaded_for_tx_batch,
programs_modified_by_tx,
programs_updated_only_for_global_cache,
feature_set,
blockhash,
lamports_per_signature,
@ -272,7 +276,8 @@ mod tests {
let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 1, 3);
let program_indices = vec![vec![2]];
let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::default()));
let programs_loaded_for_tx_batch =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default()));
let account_keys = (0..transaction_context.get_number_of_accounts())
.map(|index| {
*transaction_context
@ -308,7 +313,9 @@ mod tests {
&mut transaction_context,
rent_collector.rent,
None,
tx_executor_cache.clone(),
programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(),
&mut ExecuteTimings::default(),
@ -358,7 +365,9 @@ mod tests {
&mut transaction_context,
rent_collector.rent,
None,
tx_executor_cache.clone(),
programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(),
&mut ExecuteTimings::default(),
@ -398,7 +407,9 @@ mod tests {
&mut transaction_context,
rent_collector.rent,
None,
tx_executor_cache,
programs_loaded_for_tx_batch,
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(),
&mut ExecuteTimings::default(),
@ -496,7 +507,8 @@ mod tests {
let mut transaction_context =
TransactionContext::new(accounts, Some(Rent::default()), 1, 3);
let program_indices = vec![vec![2]];
let tx_executor_cache = Rc::new(RefCell::new(TransactionExecutorCache::default()));
let programs_loaded_for_tx_batch =
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default()));
let account_metas = vec![
AccountMeta::new(
*transaction_context.get_key_of_account_at_index(0).unwrap(),
@ -529,7 +541,9 @@ mod tests {
&mut transaction_context,
rent_collector.rent,
None,
tx_executor_cache.clone(),
programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(),
&mut ExecuteTimings::default(),
@ -563,7 +577,9 @@ mod tests {
&mut transaction_context,
rent_collector.rent,
None,
tx_executor_cache.clone(),
programs_loaded_for_tx_batch.clone(),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(),
&mut ExecuteTimings::default(),
@ -594,7 +610,9 @@ mod tests {
&mut transaction_context,
rent_collector.rent,
None,
tx_executor_cache,
programs_loaded_for_tx_batch,
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(),
&mut ExecuteTimings::default(),
@ -668,7 +686,9 @@ mod tests {
&mut transaction_context,
RentCollector::default().rent,
None,
Rc::new(RefCell::new(TransactionExecutorCache::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Rc::new(RefCell::new(LoadedProgramsForTxBatch::default())),
Arc::new(FeatureSet::all_enabled()),
ComputeBudget::default(),
&mut ExecuteTimings::default(),