* Skip updating already cached executors if unmodified
(cherry picked from commit f2ed6f09ee
)
# Conflicts:
# program-runtime/src/invoke_context.rs
# runtime/src/bank.rs
* resolve conflicts
Co-authored-by: Justin Starry <justin@solana.com>
This commit is contained in:
@ -51,7 +51,9 @@ use {
|
|||||||
inline_spl_token,
|
inline_spl_token,
|
||||||
instruction_recorder::InstructionRecorder,
|
instruction_recorder::InstructionRecorder,
|
||||||
log_collector::LogCollector,
|
log_collector::LogCollector,
|
||||||
message_processor::{ExecuteDetailsTimings, Executors, MessageProcessor},
|
message_processor::{
|
||||||
|
ExecuteDetailsTimings, Executors, MessageProcessor, TransactionExecutor,
|
||||||
|
},
|
||||||
rent_collector::RentCollector,
|
rent_collector::RentCollector,
|
||||||
stake_weighted_timestamp::{
|
stake_weighted_timestamp::{
|
||||||
calculate_stake_weighted_timestamp, MaxAllowableDrift, MAX_ALLOWABLE_DRIFT_PERCENTAGE,
|
calculate_stake_weighted_timestamp, MaxAllowableDrift, MAX_ALLOWABLE_DRIFT_PERCENTAGE,
|
||||||
@ -3328,31 +3330,39 @@ impl Bank {
|
|||||||
|
|
||||||
for key in message.account_keys.iter() {
|
for key in message.account_keys.iter() {
|
||||||
if let Some(executor) = cache.get(key) {
|
if let Some(executor) = cache.get(key) {
|
||||||
executors.insert(*key, executor);
|
executors.insert(*key, TransactionExecutor::cached(executor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for instruction_loaders in loaders.iter() {
|
for instruction_loaders in loaders.iter() {
|
||||||
for (key, _) in instruction_loaders.iter() {
|
for (key, _) in instruction_loaders.iter() {
|
||||||
if let Some(executor) = cache.get(key) {
|
if let Some(executor) = cache.get(key) {
|
||||||
executors.insert(*key, executor);
|
executors.insert(*key, TransactionExecutor::cached(executor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Rc::new(RefCell::new(Executors {
|
Rc::new(RefCell::new(executors))
|
||||||
executors,
|
|
||||||
is_dirty: false,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add executors back to the bank's cache if modified
|
/// Add executors back to the bank's cache if modified
|
||||||
fn update_executors(&self, executors: Rc<RefCell<Executors>>) {
|
fn update_executors(&self, executors: Rc<RefCell<Executors>>) {
|
||||||
let executors = executors.borrow();
|
let executors = executors.borrow();
|
||||||
if executors.is_dirty {
|
let dirty_executors: Vec<_> = executors
|
||||||
|
.iter()
|
||||||
|
.filter_map(|(key, TransactionExecutor { executor, is_dirty })| {
|
||||||
|
if *is_dirty {
|
||||||
|
Some((key, executor.clone()))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !dirty_executors.is_empty() {
|
||||||
let mut cow_cache = self.cached_executors.write().unwrap();
|
let mut cow_cache = self.cached_executors.write().unwrap();
|
||||||
let mut cache = cow_cache.write().unwrap();
|
let mut cache = cow_cache.write().unwrap();
|
||||||
for (key, executor) in executors.executors.iter() {
|
for (key, executor) in dirty_executors.into_iter() {
|
||||||
cache.put(key, (*executor).clone());
|
cache.put(key, executor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11948,50 +11958,50 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
// don't do any work if not dirty
|
// don't do any work if not dirty
|
||||||
let mut executors = Executors::default();
|
let mut executors = Executors::default();
|
||||||
executors.insert(key1, executor.clone());
|
executors.insert(key1, TransactionExecutor::cached(executor.clone()));
|
||||||
executors.insert(key2, executor.clone());
|
executors.insert(key2, TransactionExecutor::cached(executor.clone()));
|
||||||
executors.insert(key3, executor.clone());
|
executors.insert(key3, TransactionExecutor::cached(executor.clone()));
|
||||||
executors.insert(key4, executor.clone());
|
executors.insert(key4, TransactionExecutor::cached(executor.clone()));
|
||||||
let executors = Rc::new(RefCell::new(executors));
|
let executors = Rc::new(RefCell::new(executors));
|
||||||
executors.borrow_mut().is_dirty = false;
|
executors.borrow_mut().get_mut(&key1).unwrap().is_dirty = false;
|
||||||
bank.update_executors(executors);
|
bank.update_executors(executors);
|
||||||
let executors = bank.get_executors(&message, loaders);
|
let executors = bank.get_executors(&message, loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 0);
|
assert_eq!(executors.borrow().len(), 0);
|
||||||
|
|
||||||
// do work
|
// do work
|
||||||
let mut executors = Executors::default();
|
let mut executors = Executors::default();
|
||||||
executors.insert(key1, executor.clone());
|
executors.insert(key1, TransactionExecutor::dirty(executor.clone()));
|
||||||
executors.insert(key2, executor.clone());
|
executors.insert(key2, TransactionExecutor::dirty(executor.clone()));
|
||||||
executors.insert(key3, executor.clone());
|
executors.insert(key3, TransactionExecutor::dirty(executor.clone()));
|
||||||
executors.insert(key4, executor.clone());
|
executors.insert(key4, TransactionExecutor::dirty(executor.clone()));
|
||||||
let executors = Rc::new(RefCell::new(executors));
|
let executors = Rc::new(RefCell::new(executors));
|
||||||
bank.update_executors(executors);
|
bank.update_executors(executors);
|
||||||
let executors = bank.get_executors(&message, loaders);
|
let executors = bank.get_executors(&message, loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 4);
|
assert_eq!(executors.borrow().len(), 4);
|
||||||
assert!(executors.borrow().executors.contains_key(&key1));
|
assert!(executors.borrow().contains_key(&key1));
|
||||||
assert!(executors.borrow().executors.contains_key(&key2));
|
assert!(executors.borrow().contains_key(&key2));
|
||||||
assert!(executors.borrow().executors.contains_key(&key3));
|
assert!(executors.borrow().contains_key(&key3));
|
||||||
assert!(executors.borrow().executors.contains_key(&key4));
|
assert!(executors.borrow().contains_key(&key4));
|
||||||
|
|
||||||
// Check inheritance
|
// Check inheritance
|
||||||
let bank = Bank::new_from_parent(&Arc::new(bank), &solana_sdk::pubkey::new_rand(), 1);
|
let bank = Bank::new_from_parent(&Arc::new(bank), &solana_sdk::pubkey::new_rand(), 1);
|
||||||
let executors = bank.get_executors(&message, loaders);
|
let executors = bank.get_executors(&message, loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 4);
|
assert_eq!(executors.borrow().len(), 4);
|
||||||
assert!(executors.borrow().executors.contains_key(&key1));
|
assert!(executors.borrow().contains_key(&key1));
|
||||||
assert!(executors.borrow().executors.contains_key(&key2));
|
assert!(executors.borrow().contains_key(&key2));
|
||||||
assert!(executors.borrow().executors.contains_key(&key3));
|
assert!(executors.borrow().contains_key(&key3));
|
||||||
assert!(executors.borrow().executors.contains_key(&key4));
|
assert!(executors.borrow().contains_key(&key4));
|
||||||
|
|
||||||
bank.remove_executor(&key1);
|
bank.remove_executor(&key1);
|
||||||
bank.remove_executor(&key2);
|
bank.remove_executor(&key2);
|
||||||
bank.remove_executor(&key3);
|
bank.remove_executor(&key3);
|
||||||
bank.remove_executor(&key4);
|
bank.remove_executor(&key4);
|
||||||
let executors = bank.get_executors(&message, loaders);
|
let executors = bank.get_executors(&message, loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 0);
|
assert_eq!(executors.borrow().len(), 0);
|
||||||
assert!(!executors.borrow().executors.contains_key(&key1));
|
assert!(!executors.borrow().contains_key(&key1));
|
||||||
assert!(!executors.borrow().executors.contains_key(&key2));
|
assert!(!executors.borrow().contains_key(&key2));
|
||||||
assert!(!executors.borrow().executors.contains_key(&key3));
|
assert!(!executors.borrow().contains_key(&key3));
|
||||||
assert!(!executors.borrow().executors.contains_key(&key4));
|
assert!(!executors.borrow().contains_key(&key4));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -12012,36 +12022,36 @@ pub(crate) mod tests {
|
|||||||
|
|
||||||
// add one to root bank
|
// add one to root bank
|
||||||
let mut executors = Executors::default();
|
let mut executors = Executors::default();
|
||||||
executors.insert(key1, executor.clone());
|
executors.insert(key1, TransactionExecutor::dirty(executor.clone()));
|
||||||
let executors = Rc::new(RefCell::new(executors));
|
let executors = Rc::new(RefCell::new(executors));
|
||||||
root.update_executors(executors);
|
root.update_executors(executors);
|
||||||
let executors = root.get_executors(&Message::default(), loaders);
|
let executors = root.get_executors(&Message::default(), loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 1);
|
assert_eq!(executors.borrow().len(), 1);
|
||||||
|
|
||||||
let fork1 = Bank::new_from_parent(&root, &Pubkey::default(), 1);
|
let fork1 = Bank::new_from_parent(&root, &Pubkey::default(), 1);
|
||||||
let fork2 = Bank::new_from_parent(&root, &Pubkey::default(), 1);
|
let fork2 = Bank::new_from_parent(&root, &Pubkey::default(), 1);
|
||||||
|
|
||||||
let executors = fork1.get_executors(&Message::default(), loaders);
|
let executors = fork1.get_executors(&Message::default(), loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 1);
|
assert_eq!(executors.borrow().len(), 1);
|
||||||
let executors = fork2.get_executors(&Message::default(), loaders);
|
let executors = fork2.get_executors(&Message::default(), loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 1);
|
assert_eq!(executors.borrow().len(), 1);
|
||||||
|
|
||||||
let mut executors = Executors::default();
|
let mut executors = Executors::default();
|
||||||
executors.insert(key2, executor.clone());
|
executors.insert(key2, TransactionExecutor::dirty(executor.clone()));
|
||||||
let executors = Rc::new(RefCell::new(executors));
|
let executors = Rc::new(RefCell::new(executors));
|
||||||
fork1.update_executors(executors);
|
fork1.update_executors(executors);
|
||||||
|
|
||||||
let executors = fork1.get_executors(&Message::default(), loaders);
|
let executors = fork1.get_executors(&Message::default(), loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 2);
|
assert_eq!(executors.borrow().len(), 2);
|
||||||
let executors = fork2.get_executors(&Message::default(), loaders);
|
let executors = fork2.get_executors(&Message::default(), loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 1);
|
assert_eq!(executors.borrow().len(), 1);
|
||||||
|
|
||||||
fork1.remove_executor(&key1);
|
fork1.remove_executor(&key1);
|
||||||
|
|
||||||
let executors = fork1.get_executors(&Message::default(), loaders);
|
let executors = fork1.get_executors(&Message::default(), loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 1);
|
assert_eq!(executors.borrow().len(), 1);
|
||||||
let executors = fork2.get_executors(&Message::default(), loaders);
|
let executors = fork2.get_executors(&Message::default(), loaders);
|
||||||
assert_eq!(executors.borrow().executors.len(), 1);
|
assert_eq!(executors.borrow().len(), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -39,25 +39,32 @@ use {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Executors {
|
pub type Executors = HashMap<Pubkey, TransactionExecutor>;
|
||||||
pub executors: HashMap<Pubkey, Arc<dyn Executor>>,
|
|
||||||
|
/// Tracks whether a given executor is "dirty" and needs to
|
||||||
|
/// updated in the executors cache
|
||||||
|
pub struct TransactionExecutor {
|
||||||
|
pub executor: Arc<dyn Executor>,
|
||||||
pub is_dirty: bool,
|
pub is_dirty: bool,
|
||||||
}
|
}
|
||||||
impl Default for Executors {
|
|
||||||
fn default() -> Self {
|
impl TransactionExecutor {
|
||||||
|
/// Wraps an executor and tracks that it doesn't need
|
||||||
|
/// to be updated in the executors cache.
|
||||||
|
pub fn cached(executor: Arc<dyn Executor>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
executors: HashMap::default(),
|
executor,
|
||||||
is_dirty: false,
|
is_dirty: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Wraps an executor and tracks that it needs to be
|
||||||
|
/// updated in the executors cache.
|
||||||
|
pub fn dirty(executor: Arc<dyn Executor>) -> Self {
|
||||||
|
Self {
|
||||||
|
executor,
|
||||||
|
is_dirty: true,
|
||||||
}
|
}
|
||||||
impl Executors {
|
|
||||||
pub fn insert(&mut self, key: Pubkey, executor: Arc<dyn Executor>) {
|
|
||||||
let _ = self.executors.insert(key, executor);
|
|
||||||
self.is_dirty = true;
|
|
||||||
}
|
|
||||||
pub fn get(&self, key: &Pubkey) -> Option<Arc<dyn Executor>> {
|
|
||||||
self.executors.get(key).cloned()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -531,10 +538,15 @@ impl<'a> InvokeContext for ThisInvokeContext<'a> {
|
|||||||
self.compute_meter.clone()
|
self.compute_meter.clone()
|
||||||
}
|
}
|
||||||
fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
|
fn add_executor(&self, pubkey: &Pubkey, executor: Arc<dyn Executor>) {
|
||||||
self.executors.borrow_mut().insert(*pubkey, executor);
|
self.executors
|
||||||
|
.borrow_mut()
|
||||||
|
.insert(*pubkey, TransactionExecutor::dirty(executor));
|
||||||
}
|
}
|
||||||
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
fn get_executor(&self, pubkey: &Pubkey) -> Option<Arc<dyn Executor>> {
|
||||||
self.executors.borrow().get(pubkey)
|
self.executors
|
||||||
|
.borrow()
|
||||||
|
.get(pubkey)
|
||||||
|
.map(|tx_executor| tx_executor.executor.clone())
|
||||||
}
|
}
|
||||||
fn record_instruction(&self, instruction: &Instruction) {
|
fn record_instruction(&self, instruction: &Instruction) {
|
||||||
if let Some(recorder) = &self.instruction_recorder {
|
if let Some(recorder) = &self.instruction_recorder {
|
||||||
|
Reference in New Issue
Block a user