Add execute metrics (backport #22296) (#22335)

* move `ExecuteTimings` from `runtime::bank` to `program_runtime::timings`

(cherry picked from commit 7d32909e17)

# Conflicts:
#	core/Cargo.toml
#	ledger/Cargo.toml
#	programs/bpf/Cargo.lock

* Add execute metrics

(cherry picked from commit b25e4a200b)

* Add metrics for executor creation

(cherry picked from commit 848b6dfbdd)

* Add helper macro for `AddAssign`ing with saturating arithmetic

(cherry picked from commit deb9344e49)

* Use saturating_add_assign macro

(cherry picked from commit 72fc6096a0)

* Consolidate process instruction execution timings to own struct

(cherry picked from commit 390ef0fbcd)

Co-authored-by: Trent Nelson <trent@solana.com>
Co-authored-by: Carl Lin <carl@solana.com>
This commit is contained in:
mergify[bot]
2022-01-07 09:11:18 +00:00
committed by GitHub
parent 2f97fee71a
commit 7b1da62763
19 changed files with 439 additions and 116 deletions

2
Cargo.lock generated
View File

@ -5098,6 +5098,7 @@ dependencies = [
"solana-measure", "solana-measure",
"solana-metrics", "solana-metrics",
"solana-perf", "solana-perf",
"solana-program-runtime",
"solana-rayon-threadlimit", "solana-rayon-threadlimit",
"solana-runtime", "solana-runtime",
"solana-sdk", "solana-sdk",
@ -5453,6 +5454,7 @@ dependencies = [
"rustc_version 0.4.0", "rustc_version 0.4.0",
"serde", "serde",
"solana-logger 1.9.4", "solana-logger 1.9.4",
"solana-measure",
"solana-sdk", "solana-sdk",
"thiserror", "thiserror",
] ]

View File

@ -45,6 +45,7 @@ solana-metrics = { path = "../metrics", version = "=1.9.4" }
solana-net-utils = { path = "../net-utils", version = "=1.9.4" } solana-net-utils = { path = "../net-utils", version = "=1.9.4" }
solana-perf = { path = "../perf", version = "=1.9.4" } solana-perf = { path = "../perf", version = "=1.9.4" }
solana-poh = { path = "../poh", version = "=1.9.4" } solana-poh = { path = "../poh", version = "=1.9.4" }
solana-program-runtime = { path = "../program-runtime", version = "=1.9.4" }
solana-rpc = { path = "../rpc", version = "=1.9.4" } solana-rpc = { path = "../rpc", version = "=1.9.4" }
solana-replica-lib = { path = "../replica-lib", version = "=1.9.4" } solana-replica-lib = { path = "../replica-lib", version = "=1.9.4" }
solana-runtime = { path = "../runtime", version = "=1.9.4" } solana-runtime = { path = "../runtime", version = "=1.9.4" }

View File

@ -19,12 +19,10 @@ use {
perf_libs, perf_libs,
}, },
solana_poh::poh_recorder::{BankStart, PohRecorder, PohRecorderError, TransactionRecorder}, solana_poh::poh_recorder::{BankStart, PohRecorder, PohRecorderError, TransactionRecorder},
solana_program_runtime::timings::ExecuteTimings,
solana_runtime::{ solana_runtime::{
accounts_db::ErrorCounters, accounts_db::ErrorCounters,
bank::{ bank::{Bank, TransactionBalancesSet, TransactionCheckResult, TransactionExecutionResult},
Bank, ExecuteTimings, TransactionBalancesSet, TransactionCheckResult,
TransactionExecutionResult,
},
bank_utils, bank_utils,
cost_model::CostModel, cost_model::CostModel,
transaction_batch::TransactionBatch, transaction_batch::TransactionBatch,

View File

@ -6,10 +6,8 @@
use { use {
solana_ledger::blockstore::Blockstore, solana_ledger::blockstore::Blockstore,
solana_measure::measure::Measure, solana_measure::measure::Measure,
solana_runtime::{ solana_program_runtime::timings::ExecuteTimings,
bank::{Bank, ExecuteTimings}, solana_runtime::{bank::Bank, cost_model::CostModel},
cost_model::CostModel,
},
solana_sdk::timing::timestamp, solana_sdk::timing::timestamp,
std::{ std::{
sync::{ sync::{
@ -71,8 +69,12 @@ impl CostUpdateServiceTiming {
} }
pub enum CostUpdate { pub enum CostUpdate {
FrozenBank { bank: Arc<Bank> }, FrozenBank {
ExecuteTiming { execute_timings: ExecuteTimings }, bank: Arc<Bank>,
},
ExecuteTiming {
execute_timings: Box<ExecuteTimings>,
},
} }
pub type CostUpdateReceiver = Receiver<CostUpdate>; pub type CostUpdateReceiver = Receiver<CostUpdate>;

View File

@ -81,45 +81,133 @@ impl ReplaySlotStats {
i64 i64
), ),
( (
"serialize_us", "execute_details_serialize_us",
self.execute_timings.details.serialize_us, self.execute_timings.details.serialize_us,
i64 i64
), ),
( (
"create_vm_us", "execute_details_create_vm_us",
self.execute_timings.details.create_vm_us, self.execute_timings.details.create_vm_us,
i64 i64
), ),
( (
"execute_inner_us", "execute_details_execute_inner_us",
self.execute_timings.details.execute_us, self.execute_timings.details.execute_us,
i64 i64
), ),
( (
"deserialize_us", "execute_details_deserialize_us",
self.execute_timings.details.deserialize_us, self.execute_timings.details.deserialize_us,
i64 i64
), ),
( (
"changed_account_count", "execute_details_get_or_create_executor_us",
self.execute_timings.details.get_or_create_executor_us,
i64
),
(
"execute_details_changed_account_count",
self.execute_timings.details.changed_account_count, self.execute_timings.details.changed_account_count,
i64 i64
), ),
( (
"total_account_count", "execute_details_total_account_count",
self.execute_timings.details.total_account_count, self.execute_timings.details.total_account_count,
i64 i64
), ),
( (
"total_data_size", "execute_details_total_data_size",
self.execute_timings.details.total_data_size, self.execute_timings.details.total_data_size,
i64 i64
), ),
( (
"data_size_changed", "execute_details_data_size_changed",
self.execute_timings.details.data_size_changed, self.execute_timings.details.data_size_changed,
i64 i64
), ),
(
"execute_details_create_executor_register_syscalls_us",
self.execute_timings
.details
.create_executor_register_syscalls_us,
i64
),
(
"execute_details_create_executor_load_elf_us",
self.execute_timings.details.create_executor_load_elf_us,
i64
),
(
"execute_details_create_executor_verify_code_us",
self.execute_timings.details.create_executor_verify_code_us,
i64
),
(
"execute_details_create_executor_jit_compile_us",
self.execute_timings.details.create_executor_jit_compile_us,
i64
),
(
"execute_accessories_feature_set_clone_us",
self.execute_timings
.execute_accessories
.feature_set_clone_us,
i64
),
(
"execute_accessories_compute_budget_process_transaction_us",
self.execute_timings
.execute_accessories
.compute_budget_process_transaction_us,
i64
),
(
"execute_accessories_get_executors_us",
self.execute_timings.execute_accessories.get_executors_us,
i64
),
(
"execute_accessories_process_message_us",
self.execute_timings.execute_accessories.process_message_us,
i64
),
(
"execute_accessories_update_executors_us",
self.execute_timings.execute_accessories.update_executors_us,
i64
),
(
"execute_accessories_process_instructions_total_us",
self.execute_timings
.execute_accessories
.process_instructions
.total_us,
i64
),
(
"execute_accessories_process_instructions_verify_caller_us",
self.execute_timings
.execute_accessories
.process_instructions
.verify_caller_us,
i64
),
(
"execute_accessories_process_instructions_process_executable_chain_us",
self.execute_timings
.execute_accessories
.process_instructions
.process_executable_chain_us,
i64
),
(
"execute_accessories_process_instructions_verify_callee_us",
self.execute_timings
.execute_accessories
.process_instructions
.verify_callee_us,
i64
),
); );
let mut per_pubkey_timings: Vec<_> = self let mut per_pubkey_timings: Vec<_> = self

View File

@ -39,13 +39,14 @@ use {
solana_measure::measure::Measure, solana_measure::measure::Measure,
solana_metrics::inc_new_counter_info, solana_metrics::inc_new_counter_info,
solana_poh::poh_recorder::{PohRecorder, GRACE_TICKS_FACTOR, MAX_GRACE_SLOTS}, solana_poh::poh_recorder::{PohRecorder, GRACE_TICKS_FACTOR, MAX_GRACE_SLOTS},
solana_program_runtime::timings::ExecuteTimings,
solana_rpc::{ solana_rpc::{
optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender}, optimistically_confirmed_bank_tracker::{BankNotification, BankNotificationSender},
rpc_subscriptions::RpcSubscriptions, rpc_subscriptions::RpcSubscriptions,
}, },
solana_runtime::{ solana_runtime::{
accounts_background_service::AbsRequestSender, accounts_background_service::AbsRequestSender,
bank::{Bank, ExecuteTimings, NewBankOptions}, bank::{Bank, NewBankOptions},
bank_forks::BankForks, bank_forks::BankForks,
commitment::BlockCommitmentCache, commitment::BlockCommitmentCache,
vote_sender_types::ReplayVoteSender, vote_sender_types::ReplayVoteSender,
@ -2170,7 +2171,9 @@ impl ReplayStage {
// send accumulated excute-timings to cost_update_service // send accumulated excute-timings to cost_update_service
if !execute_timings.details.per_program_timings.is_empty() { if !execute_timings.details.per_program_timings.is_empty() {
cost_update_sender cost_update_sender
.send(CostUpdate::ExecuteTiming { execute_timings }) .send(CostUpdate::ExecuteTiming {
execute_timings: Box::new(execute_timings),
})
.unwrap_or_else(|err| warn!("cost_update_sender failed: {:?}", err)); .unwrap_or_else(|err| warn!("cost_update_sender failed: {:?}", err));
} }

View File

@ -40,6 +40,7 @@ solana-logger = { path = "../logger", version = "=1.9.4" }
solana-measure = { path = "../measure", version = "=1.9.4" } solana-measure = { path = "../measure", version = "=1.9.4" }
solana-metrics = { path = "../metrics", version = "=1.9.4" } solana-metrics = { path = "../metrics", version = "=1.9.4" }
solana-perf = { path = "../perf", version = "=1.9.4" } solana-perf = { path = "../perf", version = "=1.9.4" }
solana-program-runtime = { path = "../program-runtime", version = "=1.9.4" }
solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.9.4" } solana-rayon-threadlimit = { path = "../rayon-threadlimit", version = "=1.9.4" }
solana-runtime = { path = "../runtime", version = "=1.9.4" } solana-runtime = { path = "../runtime", version = "=1.9.4" }
solana-sdk = { path = "../sdk", version = "=1.9.4" } solana-sdk = { path = "../sdk", version = "=1.9.4" }

View File

@ -14,13 +14,14 @@ use {
}, },
solana_measure::measure::Measure, solana_measure::measure::Measure,
solana_metrics::{datapoint_error, inc_new_counter_debug}, solana_metrics::{datapoint_error, inc_new_counter_debug},
solana_program_runtime::timings::ExecuteTimings,
solana_rayon_threadlimit::get_thread_count, solana_rayon_threadlimit::get_thread_count,
solana_runtime::{ solana_runtime::{
accounts_db::{AccountShrinkThreshold, AccountsDbConfig}, accounts_db::{AccountShrinkThreshold, AccountsDbConfig},
accounts_index::AccountSecondaryIndexes, accounts_index::AccountSecondaryIndexes,
accounts_update_notifier_interface::AccountsUpdateNotifier, accounts_update_notifier_interface::AccountsUpdateNotifier,
bank::{ bank::{
Bank, ExecuteTimings, RentDebits, TransactionBalancesSet, TransactionExecutionResult, Bank, RentDebits, TransactionBalancesSet, TransactionExecutionResult,
TransactionResults, TransactionResults,
}, },
bank_forks::BankForks, bank_forks::BankForks,

View File

@ -20,6 +20,7 @@ num-derive = { version = "0.3" }
num-traits = { version = "0.2" } num-traits = { version = "0.2" }
serde = { version = "1.0.129", features = ["derive", "rc"] } serde = { version = "1.0.129", features = ["derive", "rc"] }
solana-logger = { path = "../logger", version = "=1.9.4" } solana-logger = { path = "../logger", version = "=1.9.4" }
solana-measure = { path = "../measure", version = "=1.9.4" }
solana-sdk = { path = "../sdk", version = "=1.9.4" } solana-sdk = { path = "../sdk", version = "=1.9.4" }
thiserror = "1.0" thiserror = "1.0"

View File

@ -1,9 +1,14 @@
use { use {
crate::{ crate::{
accounts_data_meter::AccountsDataMeter, ic_logger_msg, ic_msg, accounts_data_meter::AccountsDataMeter,
instruction_recorder::InstructionRecorder, log_collector::LogCollector, ic_logger_msg, ic_msg,
native_loader::NativeLoader, pre_account::PreAccount, timings::ExecuteDetailsTimings, instruction_recorder::InstructionRecorder,
log_collector::LogCollector,
native_loader::NativeLoader,
pre_account::PreAccount,
timings::{ExecuteDetailsTimings, ExecuteTimings},
}, },
solana_measure::measure::Measure,
solana_sdk::{ solana_sdk::{
account::{AccountSharedData, ReadableAccount}, account::{AccountSharedData, ReadableAccount},
account_utils::StateMut, account_utils::StateMut,
@ -20,6 +25,7 @@ use {
message::Message, message::Message,
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
saturating_add_assign,
sysvar::Sysvar, sysvar::Sysvar,
}, },
std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc}, std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc},
@ -567,6 +573,7 @@ impl<'a> InvokeContext<'a> {
&program_indices, &program_indices,
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
) )
.result?; .result?;
@ -709,12 +716,22 @@ impl<'a> InvokeContext<'a> {
program_indices: &[usize], program_indices: &[usize],
account_indices: &[usize], account_indices: &[usize],
caller_write_privileges: &[bool], caller_write_privileges: &[bool],
timings: &mut ExecuteTimings,
) -> ProcessInstructionResult { ) -> ProcessInstructionResult {
let is_lowest_invocation_level = self.invoke_stack.is_empty(); let is_lowest_invocation_level = self.invoke_stack.is_empty();
if !is_lowest_invocation_level { if !is_lowest_invocation_level {
// Verify the calling program hasn't misbehaved // Verify the calling program hasn't misbehaved
let mut verify_caller_time = Measure::start("verify_caller_time");
let result = let result =
self.verify_and_update(instruction, account_indices, caller_write_privileges); self.verify_and_update(instruction, account_indices, caller_write_privileges);
verify_caller_time.stop();
saturating_add_assign!(
timings
.execute_accessories
.process_instructions
.verify_caller_us,
verify_caller_time.as_us()
);
if result.is_err() { if result.is_err() {
return ProcessInstructionResult { return ProcessInstructionResult {
compute_units_consumed: 0, compute_units_consumed: 0,
@ -727,22 +744,45 @@ impl<'a> InvokeContext<'a> {
let result = self let result = self
.push(message, instruction, program_indices, account_indices) .push(message, instruction, program_indices, account_indices)
.and_then(|_| { .and_then(|_| {
let mut process_executable_chain_time =
Measure::start("process_executable_chain_time");
self.return_data = (*instruction.program_id(&message.account_keys), Vec::new()); self.return_data = (*instruction.program_id(&message.account_keys), Vec::new());
let pre_remaining_units = self.compute_meter.borrow().get_remaining(); let pre_remaining_units = self.compute_meter.borrow().get_remaining();
let execution_result = self.process_executable_chain(&instruction.data); let execution_result = self.process_executable_chain(&instruction.data);
let post_remaining_units = self.compute_meter.borrow().get_remaining(); let post_remaining_units = self.compute_meter.borrow().get_remaining();
compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units); compute_units_consumed = pre_remaining_units.saturating_sub(post_remaining_units);
execution_result?; process_executable_chain_time.stop();
// Verify the called program has not misbehaved // Verify the called program has not misbehaved
if is_lowest_invocation_level { let mut verify_callee_time = Measure::start("verify_callee_time");
self.verify(message, instruction, program_indices) let result = execution_result.and_then(|_| {
} else { if is_lowest_invocation_level {
let write_privileges: Vec<bool> = (0..message.account_keys.len()) self.verify(message, instruction, program_indices)
.map(|i| message.is_writable(i)) } else {
.collect(); let write_privileges: Vec<bool> = (0..message.account_keys.len())
self.verify_and_update(instruction, account_indices, &write_privileges) .map(|i| message.is_writable(i))
} .collect();
self.verify_and_update(instruction, account_indices, &write_privileges)
}
});
verify_callee_time.stop();
saturating_add_assign!(
timings
.execute_accessories
.process_instructions
.process_executable_chain_us,
process_executable_chain_time.as_us()
);
saturating_add_assign!(
timings
.execute_accessories
.process_instructions
.verify_callee_us,
verify_callee_time.as_us()
);
result
}); });
// Pop the invoke_stack to restore previous state // Pop the invoke_stack to restore previous state
@ -1387,6 +1427,7 @@ mod tests {
&program_indices[1..], &program_indices[1..],
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
) )
.result, .result,
Err(InstructionError::ExternalAccountDataModified) Err(InstructionError::ExternalAccountDataModified)
@ -1403,6 +1444,7 @@ mod tests {
&program_indices[1..], &program_indices[1..],
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
) )
.result, .result,
Err(InstructionError::ReadonlyDataModified) Err(InstructionError::ReadonlyDataModified)
@ -1461,6 +1503,7 @@ mod tests {
&program_indices[1..], &program_indices[1..],
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
), ),
case.1 case.1
); );
@ -1712,6 +1755,7 @@ mod tests {
&program_indices[1..], &program_indices[1..],
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
); );
// Because the instruction had compute cost > 0, then regardless of the execution result, // Because the instruction had compute cost > 0, then regardless of the execution result,
@ -1786,6 +1830,7 @@ mod tests {
&program_indices, &program_indices,
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
) )
.result; .result;
@ -1816,6 +1861,7 @@ mod tests {
&program_indices, &program_indices,
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
) )
.result; .result;
@ -1846,6 +1892,7 @@ mod tests {
&program_indices, &program_indices,
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
) )
.result; .result;

View File

@ -1,4 +1,7 @@
use {solana_sdk::pubkey::Pubkey, std::collections::HashMap}; use {
solana_sdk::{pubkey::Pubkey, saturating_add_assign},
std::collections::HashMap,
};
#[derive(Default, Debug, PartialEq)] #[derive(Default, Debug, PartialEq)]
pub struct ProgramTiming { pub struct ProgramTiming {
@ -15,23 +18,97 @@ impl ProgramTiming {
for tx_error_compute_consumed in self.errored_txs_compute_consumed.drain(..) { for tx_error_compute_consumed in self.errored_txs_compute_consumed.drain(..) {
let compute_units_update = let compute_units_update =
std::cmp::max(current_estimated_program_cost, tx_error_compute_consumed); std::cmp::max(current_estimated_program_cost, tx_error_compute_consumed);
self.accumulated_units = self.accumulated_units.saturating_add(compute_units_update); saturating_add_assign!(self.accumulated_units, compute_units_update);
self.count = self.count.saturating_add(1); saturating_add_assign!(self.count, 1);
} }
} }
pub fn accumulate_program_timings(&mut self, other: &ProgramTiming) { pub fn accumulate_program_timings(&mut self, other: &ProgramTiming) {
self.accumulated_us = self.accumulated_us.saturating_add(other.accumulated_us); saturating_add_assign!(self.accumulated_us, other.accumulated_us);
self.accumulated_units = self saturating_add_assign!(self.accumulated_units, other.accumulated_units);
.accumulated_units saturating_add_assign!(self.count, other.count);
.saturating_add(other.accumulated_units);
self.count = self.count.saturating_add(other.count);
// Clones the entire vector, maybe not great... // Clones the entire vector, maybe not great...
self.errored_txs_compute_consumed self.errored_txs_compute_consumed
.extend(other.errored_txs_compute_consumed.clone()); .extend(other.errored_txs_compute_consumed.clone());
self.total_errored_units = self saturating_add_assign!(self.total_errored_units, other.total_errored_units);
.total_errored_units }
.saturating_add(other.total_errored_units); }
#[derive(Default, Debug)]
pub struct ExecuteTimings {
pub check_us: u64,
pub load_us: u64,
pub execute_us: u64,
pub store_us: u64,
pub update_stakes_cache_us: u64,
pub total_batches_len: usize,
pub num_execute_batches: u64,
pub collect_logs_us: u64,
pub details: ExecuteDetailsTimings,
pub execute_accessories: ExecuteAccessoryTimings,
}
impl ExecuteTimings {
pub fn accumulate(&mut self, other: &ExecuteTimings) {
saturating_add_assign!(self.check_us, other.check_us);
saturating_add_assign!(self.load_us, other.load_us);
saturating_add_assign!(self.execute_us, other.execute_us);
saturating_add_assign!(self.store_us, other.store_us);
saturating_add_assign!(self.update_stakes_cache_us, other.update_stakes_cache_us);
saturating_add_assign!(self.total_batches_len, other.total_batches_len);
saturating_add_assign!(self.num_execute_batches, other.num_execute_batches);
saturating_add_assign!(self.collect_logs_us, other.collect_logs_us);
self.details.accumulate(&other.details);
self.execute_accessories
.accumulate(&other.execute_accessories);
}
}
#[derive(Default, Debug)]
pub struct ExecuteProcessInstructionTimings {
pub total_us: u64,
pub verify_caller_us: u64,
pub process_executable_chain_us: u64,
pub verify_callee_us: u64,
}
impl ExecuteProcessInstructionTimings {
pub fn accumulate(&mut self, other: &ExecuteProcessInstructionTimings) {
saturating_add_assign!(self.total_us, other.total_us);
saturating_add_assign!(self.verify_caller_us, other.verify_caller_us);
saturating_add_assign!(
self.process_executable_chain_us,
other.process_executable_chain_us
);
saturating_add_assign!(self.verify_callee_us, other.verify_callee_us);
}
}
#[derive(Default, Debug)]
pub struct ExecuteAccessoryTimings {
pub feature_set_clone_us: u64,
pub compute_budget_process_transaction_us: u64,
pub get_executors_us: u64,
pub process_message_us: u64,
pub update_executors_us: u64,
pub process_instructions: ExecuteProcessInstructionTimings,
}
impl ExecuteAccessoryTimings {
pub fn accumulate(&mut self, other: &ExecuteAccessoryTimings) {
saturating_add_assign!(
self.compute_budget_process_transaction_us,
other.feature_set_clone_us
);
saturating_add_assign!(
self.compute_budget_process_transaction_us,
other.compute_budget_process_transaction_us
);
saturating_add_assign!(self.get_executors_us, other.get_executors_us);
saturating_add_assign!(self.process_message_us, other.process_message_us);
saturating_add_assign!(self.update_executors_us, other.update_executors_us);
self.process_instructions
.accumulate(&other.process_instructions);
} }
} }
@ -41,28 +118,47 @@ pub struct ExecuteDetailsTimings {
pub create_vm_us: u64, pub create_vm_us: u64,
pub execute_us: u64, pub execute_us: u64,
pub deserialize_us: u64, pub deserialize_us: u64,
pub get_or_create_executor_us: u64,
pub changed_account_count: u64, pub changed_account_count: u64,
pub total_account_count: u64, pub total_account_count: u64,
pub total_data_size: usize, pub total_data_size: usize,
pub data_size_changed: usize, pub data_size_changed: usize,
pub create_executor_register_syscalls_us: u64,
pub create_executor_load_elf_us: u64,
pub create_executor_verify_code_us: u64,
pub create_executor_jit_compile_us: u64,
pub per_program_timings: HashMap<Pubkey, ProgramTiming>, pub per_program_timings: HashMap<Pubkey, ProgramTiming>,
} }
impl ExecuteDetailsTimings { impl ExecuteDetailsTimings {
pub fn accumulate(&mut self, other: &ExecuteDetailsTimings) { pub fn accumulate(&mut self, other: &ExecuteDetailsTimings) {
self.serialize_us = self.serialize_us.saturating_add(other.serialize_us); saturating_add_assign!(self.serialize_us, other.serialize_us);
self.create_vm_us = self.create_vm_us.saturating_add(other.create_vm_us); saturating_add_assign!(self.create_vm_us, other.create_vm_us);
self.execute_us = self.execute_us.saturating_add(other.execute_us); saturating_add_assign!(self.execute_us, other.execute_us);
self.deserialize_us = self.deserialize_us.saturating_add(other.deserialize_us); saturating_add_assign!(self.deserialize_us, other.deserialize_us);
self.changed_account_count = self saturating_add_assign!(
.changed_account_count self.get_or_create_executor_us,
.saturating_add(other.changed_account_count); other.get_or_create_executor_us
self.total_account_count = self );
.total_account_count saturating_add_assign!(self.changed_account_count, other.changed_account_count);
.saturating_add(other.total_account_count); saturating_add_assign!(self.total_account_count, other.total_account_count);
self.total_data_size = self.total_data_size.saturating_add(other.total_data_size); saturating_add_assign!(self.total_data_size, other.total_data_size);
self.data_size_changed = self saturating_add_assign!(self.data_size_changed, other.data_size_changed);
.data_size_changed saturating_add_assign!(
.saturating_add(other.data_size_changed); self.create_executor_register_syscalls_us,
other.create_executor_register_syscalls_us
);
saturating_add_assign!(
self.create_executor_load_elf_us,
other.create_executor_load_elf_us
);
saturating_add_assign!(
self.create_executor_verify_code_us,
other.create_executor_verify_code_us
);
saturating_add_assign!(
self.create_executor_jit_compile_us,
other.create_executor_jit_compile_us
);
for (id, other) in &other.per_program_timings { for (id, other) in &other.per_program_timings {
let program_timing = self.per_program_timings.entry(*id).or_default(); let program_timing = self.per_program_timings.entry(*id).or_default();
program_timing.accumulate_program_timings(other); program_timing.accumulate_program_timings(other);

View File

@ -11,9 +11,11 @@ use {
log::*, log::*,
solana_banks_client::start_client, solana_banks_client::start_client,
solana_banks_server::banks_server::start_local_server, solana_banks_server::banks_server::start_local_server,
solana_program_runtime::{ic_msg, invoke_context::ProcessInstructionWithContext, stable_log}, solana_program_runtime::{
ic_msg, invoke_context::ProcessInstructionWithContext, stable_log, timings::ExecuteTimings,
},
solana_runtime::{ solana_runtime::{
bank::{Bank, ExecuteTimings}, bank::Bank,
bank_forks::BankForks, bank_forks::BankForks,
builtins::Builtin, builtins::Builtin,
commitment::BlockCommitmentCache, commitment::BlockCommitmentCache,
@ -321,6 +323,7 @@ impl solana_sdk::program_stubs::SyscallStubs for SyscallStubs {
&program_indices, &program_indices,
&account_indices, &account_indices,
&caller_privileges, &caller_privileges,
&mut ExecuteTimings::default(),
) )
.result .result
.map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?; .map_err(|err| ProgramError::try_from(err).unwrap_or_else(|err| panic!("{}", err)))?;

View File

@ -3256,6 +3256,7 @@ dependencies = [
"rustc_version 0.4.0", "rustc_version 0.4.0",
"serde", "serde",
"solana-logger 1.9.4", "solana-logger 1.9.4",
"solana-measure",
"solana-sdk", "solana-sdk",
"thiserror", "thiserror",
] ]

View File

@ -17,7 +17,7 @@ use solana_bpf_loader_program::{
use solana_bpf_rust_invoke::instructions::*; use solana_bpf_rust_invoke::instructions::*;
use solana_bpf_rust_realloc::instructions::*; use solana_bpf_rust_realloc::instructions::*;
use solana_bpf_rust_realloc_invoke::instructions::*; use solana_bpf_rust_realloc_invoke::instructions::*;
use solana_program_runtime::invoke_context::with_mock_invoke_context; use solana_program_runtime::{invoke_context::with_mock_invoke_context, timings::ExecuteTimings};
use solana_rbpf::{ use solana_rbpf::{
elf::Executable, elf::Executable,
static_analysis::Analysis, static_analysis::Analysis,
@ -25,7 +25,7 @@ use solana_rbpf::{
}; };
use solana_runtime::{ use solana_runtime::{
bank::{ bank::{
Bank, DurableNonceFee, ExecuteTimings, TransactionBalancesSet, TransactionExecutionDetails, Bank, DurableNonceFee, TransactionBalancesSet, TransactionExecutionDetails,
TransactionExecutionResult, TransactionResults, TransactionExecutionResult, TransactionResults,
}, },
bank_client::BankClient, bank_client::BankClient,

View File

@ -51,6 +51,7 @@ use {
program_utils::limited_deserialize, program_utils::limited_deserialize,
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
saturating_add_assign,
system_instruction::{self, MAX_PERMITTED_DATA_LENGTH}, system_instruction::{self, MAX_PERMITTED_DATA_LENGTH},
}, },
std::{cell::RefCell, fmt::Debug, pin::Pin, rc::Rc, sync::Arc}, std::{cell::RefCell, fmt::Debug, pin::Pin, rc::Rc, sync::Arc},
@ -85,7 +86,14 @@ pub fn create_executor(
use_jit: bool, use_jit: bool,
reject_deployment_of_broken_elfs: bool, reject_deployment_of_broken_elfs: bool,
) -> Result<Arc<BpfExecutor>, InstructionError> { ) -> Result<Arc<BpfExecutor>, InstructionError> {
let syscall_registry = syscalls::register_syscalls(invoke_context).map_err(|e| { let mut register_syscalls_time = Measure::start("register_syscalls_time");
let register_syscall_result = syscalls::register_syscalls(invoke_context);
register_syscalls_time.stop();
invoke_context.timings.create_executor_register_syscalls_us = invoke_context
.timings
.create_executor_register_syscalls_us
.saturating_add(register_syscalls_time.as_us());
let syscall_registry = register_syscall_result.map_err(|e| {
ic_msg!(invoke_context, "Failed to register syscalls: {}", e); ic_msg!(invoke_context, "Failed to register syscalls: {}", e);
InstructionError::ProgramEnvironmentSetupFailure InstructionError::ProgramEnvironmentSetupFailure
})?; })?;
@ -116,20 +124,40 @@ pub fn create_executor(
let mut executable = { let mut executable = {
let keyed_accounts = invoke_context.get_keyed_accounts()?; let keyed_accounts = invoke_context.get_keyed_accounts()?;
let programdata = keyed_account_at_index(keyed_accounts, programdata_account_index)?; let programdata = keyed_account_at_index(keyed_accounts, programdata_account_index)?;
Executable::<BpfError, ThisInstructionMeter>::from_elf( let mut load_elf_time = Measure::start("load_elf_time");
let executable = Executable::<BpfError, ThisInstructionMeter>::from_elf(
&programdata.try_account_ref()?.data()[programdata_offset..], &programdata.try_account_ref()?.data()[programdata_offset..],
None, None,
config, config,
syscall_registry, syscall_registry,
) );
load_elf_time.stop();
invoke_context.timings.create_executor_load_elf_us = invoke_context
.timings
.create_executor_load_elf_us
.saturating_add(load_elf_time.as_us());
executable
} }
.map_err(|e| map_ebpf_error(invoke_context, e))?; .map_err(|e| map_ebpf_error(invoke_context, e))?;
let text_bytes = executable.get_text_bytes().1; let text_bytes = executable.get_text_bytes().1;
let mut verify_code_time = Measure::start("verify_code_time");
verifier::check(text_bytes, &config) verifier::check(text_bytes, &config)
.map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e.into())))?; .map_err(|e| map_ebpf_error(invoke_context, EbpfError::UserError(e.into())))?;
verify_code_time.stop();
invoke_context.timings.create_executor_verify_code_us = invoke_context
.timings
.create_executor_verify_code_us
.saturating_add(verify_code_time.as_us());
if use_jit { if use_jit {
if let Err(err) = Executable::<BpfError, ThisInstructionMeter>::jit_compile(&mut executable) let mut jit_compile_time = Measure::start("jit_compile_time");
{ let jit_compile_result =
Executable::<BpfError, ThisInstructionMeter>::jit_compile(&mut executable);
jit_compile_time.stop();
invoke_context.timings.create_executor_jit_compile_us = invoke_context
.timings
.create_executor_jit_compile_us
.saturating_add(jit_compile_time.as_us());
if let Err(err) = jit_compile_result {
ic_msg!(invoke_context, "Failed to compile program {:?}", err); ic_msg!(invoke_context, "Failed to compile program {:?}", err);
return Err(InstructionError::ProgramFailedToCompile); return Err(InstructionError::ProgramFailedToCompile);
} }
@ -291,6 +319,7 @@ fn process_instruction_common(
0 0
}; };
let mut get_or_create_executor_time = Measure::start("get_or_create_executor_time");
let executor = match invoke_context.get_executor(program_id) { let executor = match invoke_context.get_executor(program_id) {
Some(executor) => executor, Some(executor) => executor,
None => { None => {
@ -306,6 +335,12 @@ fn process_instruction_common(
executor executor
} }
}; };
get_or_create_executor_time.stop();
saturating_add_assign!(
invoke_context.timings.get_or_create_executor_us,
get_or_create_executor_time.as_us()
);
executor.execute( executor.execute(
next_first_instruction_account, next_first_instruction_account,
instruction_data, instruction_data,

View File

@ -7,6 +7,7 @@ use {
ic_logger_msg, ic_msg, ic_logger_msg, ic_msg,
invoke_context::{ComputeMeter, InvokeContext}, invoke_context::{ComputeMeter, InvokeContext},
stable_log, stable_log,
timings::ExecuteTimings,
}, },
solana_rbpf::{ solana_rbpf::{
aligned_memory::AlignedMemory, aligned_memory::AlignedMemory,
@ -2371,6 +2372,7 @@ fn call<'a, 'b: 'a>(
&program_indices, &program_indices,
&account_indices, &account_indices,
&caller_write_privileges, &caller_write_privileges,
&mut ExecuteTimings::default(),
) )
.result .result
.map_err(SyscallError::InstructionError)?; .map_err(SyscallError::InstructionError)?;

View File

@ -82,7 +82,7 @@ use {
TransactionAccountRefCells, TransactionExecutor, TransactionAccountRefCells, TransactionExecutor,
}, },
log_collector::LogCollector, log_collector::LogCollector,
timings::ExecuteDetailsTimings, timings::ExecuteTimings,
}, },
solana_sdk::{ solana_sdk::{
account::{ account::{
@ -119,7 +119,7 @@ use {
precompiles::get_precompiles, precompiles::get_precompiles,
program_utils::limited_deserialize, program_utils::limited_deserialize,
pubkey::Pubkey, pubkey::Pubkey,
secp256k1_program, saturating_add_assign, secp256k1_program,
signature::{Keypair, Signature}, signature::{Keypair, Signature},
slot_hashes::SlotHashes, slot_hashes::SlotHashes,
slot_history::SlotHistory, slot_history::SlotHistory,
@ -212,36 +212,6 @@ impl RentDebits {
} }
} }
#[derive(Default, Debug)]
pub struct ExecuteTimings {
pub check_us: u64,
pub load_us: u64,
pub execute_us: u64,
pub store_us: u64,
pub update_stakes_cache_us: u64,
pub total_batches_len: usize,
pub num_execute_batches: u64,
pub details: ExecuteDetailsTimings,
}
impl ExecuteTimings {
pub fn accumulate(&mut self, other: &ExecuteTimings) {
self.check_us = self.check_us.saturating_add(other.check_us);
self.load_us = self.load_us.saturating_add(other.load_us);
self.execute_us = self.execute_us.saturating_add(other.execute_us);
self.store_us = self.store_us.saturating_add(other.store_us);
self.update_stakes_cache_us = self
.update_stakes_cache_us
.saturating_add(other.update_stakes_cache_us);
self.total_batches_len = self
.total_batches_len
.saturating_add(other.total_batches_len);
self.num_execute_batches = self
.num_execute_batches
.saturating_add(other.num_execute_batches);
self.details.accumulate(&other.details);
}
}
type BankStatusCache = StatusCache<Result<()>>; type BankStatusCache = StatusCache<Result<()>>;
#[frozen_abi(digest = "6XG6H1FChrDdY39K62KFWj5XfDao4dd24WZgcJkdMu1E")] #[frozen_abi(digest = "6XG6H1FChrDdY39K62KFWj5XfDao4dd24WZgcJkdMu1E")]
pub type BankSlotDelta = SlotDelta<Result<()>>; pub type BankSlotDelta = SlotDelta<Result<()>>;
@ -3607,7 +3577,7 @@ impl Bank {
durable_nonce_fee: Option<DurableNonceFee>, durable_nonce_fee: Option<DurableNonceFee>,
enable_cpi_recording: bool, enable_cpi_recording: bool,
enable_log_recording: bool, enable_log_recording: bool,
execute_details_timings: &mut ExecuteDetailsTimings, timings: &mut ExecuteTimings,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> TransactionExecutionResult { ) -> TransactionExecutionResult {
let legacy_message = match tx.message().legacy_message() { let legacy_message = match tx.message().legacy_message() {
@ -3620,11 +3590,17 @@ impl Bank {
} }
}; };
let mut get_executors_time = Measure::start("get_executors_time");
let executors = self.get_executors( let executors = self.get_executors(
tx.message(), tx.message(),
&loaded_transaction.accounts, &loaded_transaction.accounts,
&loaded_transaction.program_indices, &loaded_transaction.program_indices,
); );
get_executors_time.stop();
saturating_add_assign!(
timings.execute_accessories.get_executors_us,
get_executors_time.as_us()
);
let account_refcells = Self::accounts_to_refcells(&mut loaded_transaction.accounts); let account_refcells = Self::accounts_to_refcells(&mut loaded_transaction.accounts);
@ -3644,6 +3620,8 @@ impl Bank {
}; };
let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature(); let (blockhash, lamports_per_signature) = self.last_blockhash_and_lamports_per_signature();
let mut process_message_time = Measure::start("process_message_time");
let process_result = MessageProcessor::process_message( let process_result = MessageProcessor::process_message(
&self.builtin_programs.vec, &self.builtin_programs.vec,
legacy_message, legacy_message,
@ -3655,14 +3633,25 @@ impl Bank {
instruction_recorders.as_deref(), instruction_recorders.as_deref(),
self.feature_set.clone(), self.feature_set.clone(),
compute_budget, compute_budget,
execute_details_timings, timings,
&*self.sysvar_cache.read().unwrap(), &*self.sysvar_cache.read().unwrap(),
blockhash, blockhash,
lamports_per_signature, lamports_per_signature,
self.load_accounts_data_len(), self.load_accounts_data_len(),
); );
process_message_time.stop();
saturating_add_assign!(
timings.execute_accessories.process_message_us,
process_message_time.as_us()
);
let mut update_executors_time = Measure::start("update_executors_time");
self.update_executors(process_result.is_ok(), executors); self.update_executors(process_result.is_ok(), executors);
update_executors_time.stop();
saturating_add_assign!(
timings.execute_accessories.update_executors_us,
update_executors_time.as_us()
);
let status = process_result let status = process_result
.map(|info| { .map(|info| {
@ -3763,19 +3752,36 @@ impl Bank {
let mut execution_time = Measure::start("execution_time"); let mut execution_time = Measure::start("execution_time");
let mut signature_count: u64 = 0; let mut signature_count: u64 = 0;
let execute_details_timings = &mut timings.details;
let execution_results: Vec<TransactionExecutionResult> = loaded_txs let execution_results: Vec<TransactionExecutionResult> = loaded_txs
.iter_mut() .iter_mut()
.zip(sanitized_txs.iter()) .zip(sanitized_txs.iter())
.map(|(accs, tx)| match accs { .map(|(accs, tx)| match accs {
(Err(e), _nonce) => TransactionExecutionResult::NotExecuted(e.clone()), (Err(e), _nonce) => TransactionExecutionResult::NotExecuted(e.clone()),
(Ok(loaded_transaction), nonce) => { (Ok(loaded_transaction), nonce) => {
let mut feature_set_clone_time = Measure::start("feature_set_clone");
let feature_set = self.feature_set.clone(); let feature_set = self.feature_set.clone();
feature_set_clone_time.stop();
saturating_add_assign!(
timings.execute_accessories.feature_set_clone_us,
feature_set_clone_time.as_us()
);
signature_count += u64::from(tx.message().header().num_required_signatures); signature_count += u64::from(tx.message().header().num_required_signatures);
let mut compute_budget = self.compute_budget.unwrap_or_else(ComputeBudget::new); let mut compute_budget = self.compute_budget.unwrap_or_else(ComputeBudget::new);
if feature_set.is_active(&tx_wide_compute_cap::id()) { if feature_set.is_active(&tx_wide_compute_cap::id()) {
if let Err(err) = compute_budget.process_transaction(tx, feature_set) { let mut compute_budget_process_transaction_time =
Measure::start("compute_budget_process_transaction_time");
let process_transaction_result =
compute_budget.process_transaction(tx, feature_set);
compute_budget_process_transaction_time.stop();
saturating_add_assign!(
timings
.execute_accessories
.compute_budget_process_transaction_us,
compute_budget_process_transaction_time.as_us()
);
if let Err(err) = process_transaction_result {
return TransactionExecutionResult::NotExecuted(err); return TransactionExecutionResult::NotExecuted(err);
} }
} }
@ -3789,7 +3795,7 @@ impl Bank {
durable_nonce_fee, durable_nonce_fee,
enable_cpi_recording, enable_cpi_recording,
enable_log_recording, enable_log_recording,
execute_details_timings, timings,
&mut error_counters, &mut error_counters,
) )
} }

View File

@ -8,7 +8,7 @@ use {
TransactionAccountRefCell, TransactionAccountRefCell,
}, },
log_collector::LogCollector, log_collector::LogCollector,
timings::ExecuteDetailsTimings, timings::ExecuteTimings,
}, },
solana_sdk::{ solana_sdk::{
account::WritableAccount, account::WritableAccount,
@ -19,6 +19,7 @@ use {
precompiles::is_precompile, precompiles::is_precompile,
pubkey::Pubkey, pubkey::Pubkey,
rent::Rent, rent::Rent,
saturating_add_assign,
sysvar::instructions, sysvar::instructions,
transaction::TransactionError, transaction::TransactionError,
}, },
@ -62,7 +63,7 @@ impl MessageProcessor {
instruction_recorders: Option<&[InstructionRecorder]>, instruction_recorders: Option<&[InstructionRecorder]>,
feature_set: Arc<FeatureSet>, feature_set: Arc<FeatureSet>,
compute_budget: ComputeBudget, compute_budget: ComputeBudget,
timings: &mut ExecuteDetailsTimings, timings: &mut ExecuteTimings,
sysvars: &[(Pubkey, Vec<u8>)], sysvars: &[(Pubkey, Vec<u8>)],
blockhash: Hash, blockhash: Hash,
lamports_per_signature: u64, lamports_per_signature: u64,
@ -120,15 +121,26 @@ impl MessageProcessor {
let ProcessInstructionResult { let ProcessInstructionResult {
compute_units_consumed, compute_units_consumed,
result, result,
} = invoke_context.process_instruction(message, instruction, program_indices, &[], &[]); } = invoke_context.process_instruction(
message,
instruction,
program_indices,
&[],
&[],
timings,
);
time.stop(); time.stop();
timings.accumulate_program( timings.details.accumulate_program(
instruction.program_id(&message.account_keys), instruction.program_id(&message.account_keys),
time.as_us(), time.as_us(),
compute_units_consumed, compute_units_consumed,
result.is_err(), result.is_err(),
); );
timings.accumulate(&invoke_context.timings); timings.details.accumulate(&invoke_context.timings);
saturating_add_assign!(
timings.execute_accessories.process_instructions.total_us,
time.as_us()
);
result result
.map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?; .map_err(|err| TransactionError::InstructionError(instruction_index as u8, err))?;
} }
@ -255,7 +267,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
&mut ExecuteDetailsTimings::default(), &mut ExecuteTimings::default(),
&[], &[],
Hash::default(), Hash::default(),
0, 0,
@ -285,7 +297,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
&mut ExecuteDetailsTimings::default(), &mut ExecuteTimings::default(),
&[], &[],
Hash::default(), Hash::default(),
0, 0,
@ -319,7 +331,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
&mut ExecuteDetailsTimings::default(), &mut ExecuteTimings::default(),
&[], &[],
Hash::default(), Hash::default(),
0, 0,
@ -464,7 +476,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
&mut ExecuteDetailsTimings::default(), &mut ExecuteTimings::default(),
&[], &[],
Hash::default(), Hash::default(),
0, 0,
@ -498,7 +510,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
&mut ExecuteDetailsTimings::default(), &mut ExecuteTimings::default(),
&[], &[],
Hash::default(), Hash::default(),
0, 0,
@ -529,7 +541,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
&mut ExecuteDetailsTimings::default(), &mut ExecuteTimings::default(),
&[], &[],
Hash::default(), Hash::default(),
0, 0,
@ -587,7 +599,7 @@ mod tests {
None, None,
Arc::new(FeatureSet::all_enabled()), Arc::new(FeatureSet::all_enabled()),
ComputeBudget::new(), ComputeBudget::new(),
&mut ExecuteDetailsTimings::default(), &mut ExecuteTimings::default(),
&[], &[],
Hash::default(), Hash::default(),
0, 0,

View File

@ -103,6 +103,15 @@ macro_rules! program_stubs {
() => {}; () => {};
} }
/// Convenience macro for `AddAssign` with saturating arithmetic.
/// Replace by `std::num::Saturating` once stable
#[macro_export]
macro_rules! saturating_add_assign {
($i:expr, $v:expr) => {{
$i = $i.saturating_add($v)
}};
}
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;
pub extern crate bs58; pub extern crate bs58;
@ -110,3 +119,18 @@ extern crate log as logger;
#[macro_use] #[macro_use]
extern crate solana_frozen_abi_macro; extern crate solana_frozen_abi_macro;
#[cfg(test)]
mod tests {
#[test]
fn test_saturating_add_assign() {
let mut i = 0u64;
let v = 1;
saturating_add_assign!(i, v);
assert_eq!(i, 1);
i = u64::MAX;
saturating_add_assign!(i, v);
assert_eq!(i, u64::MAX);
}
}