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

View File

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

View File

@ -1,9 +1,14 @@
use {
crate::{
accounts_data_meter::AccountsDataMeter, ic_logger_msg, ic_msg,
instruction_recorder::InstructionRecorder, log_collector::LogCollector,
native_loader::NativeLoader, pre_account::PreAccount, timings::ExecuteDetailsTimings,
accounts_data_meter::AccountsDataMeter,
ic_logger_msg, ic_msg,
instruction_recorder::InstructionRecorder,
log_collector::LogCollector,
native_loader::NativeLoader,
pre_account::PreAccount,
timings::{ExecuteDetailsTimings, ExecuteTimings},
},
solana_measure::measure::Measure,
solana_sdk::{
account::{AccountSharedData, ReadableAccount},
account_utils::StateMut,
@ -20,6 +25,7 @@ use {
message::Message,
pubkey::Pubkey,
rent::Rent,
saturating_add_assign,
sysvar::Sysvar,
},
std::{cell::RefCell, collections::HashMap, fmt::Debug, rc::Rc, sync::Arc},
@ -567,6 +573,7 @@ impl<'a> InvokeContext<'a> {
&program_indices,
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
)
.result?;
@ -709,12 +716,22 @@ impl<'a> InvokeContext<'a> {
program_indices: &[usize],
account_indices: &[usize],
caller_write_privileges: &[bool],
timings: &mut ExecuteTimings,
) -> ProcessInstructionResult {
let is_lowest_invocation_level = self.invoke_stack.is_empty();
if !is_lowest_invocation_level {
// Verify the calling program hasn't misbehaved
let mut verify_caller_time = Measure::start("verify_caller_time");
let result =
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() {
return ProcessInstructionResult {
compute_units_consumed: 0,
@ -727,22 +744,45 @@ impl<'a> InvokeContext<'a> {
let result = self
.push(message, instruction, program_indices, account_indices)
.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());
let pre_remaining_units = self.compute_meter.borrow().get_remaining();
let execution_result = self.process_executable_chain(&instruction.data);
let post_remaining_units = self.compute_meter.borrow().get_remaining();
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
if is_lowest_invocation_level {
self.verify(message, instruction, program_indices)
} else {
let write_privileges: Vec<bool> = (0..message.account_keys.len())
.map(|i| message.is_writable(i))
.collect();
self.verify_and_update(instruction, account_indices, &write_privileges)
}
let mut verify_callee_time = Measure::start("verify_callee_time");
let result = execution_result.and_then(|_| {
if is_lowest_invocation_level {
self.verify(message, instruction, program_indices)
} else {
let write_privileges: Vec<bool> = (0..message.account_keys.len())
.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
@ -1387,6 +1427,7 @@ mod tests {
&program_indices[1..],
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
)
.result,
Err(InstructionError::ExternalAccountDataModified)
@ -1403,6 +1444,7 @@ mod tests {
&program_indices[1..],
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
)
.result,
Err(InstructionError::ReadonlyDataModified)
@ -1461,6 +1503,7 @@ mod tests {
&program_indices[1..],
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
),
case.1
);
@ -1712,6 +1755,7 @@ mod tests {
&program_indices[1..],
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
);
// Because the instruction had compute cost > 0, then regardless of the execution result,
@ -1786,6 +1830,7 @@ mod tests {
&program_indices,
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
)
.result;
@ -1816,6 +1861,7 @@ mod tests {
&program_indices,
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
)
.result;
@ -1846,6 +1892,7 @@ mod tests {
&program_indices,
&account_indices,
&caller_write_privileges,
&mut ExecuteTimings::default(),
)
.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)]
pub struct ProgramTiming {
@ -15,23 +18,97 @@ impl ProgramTiming {
for tx_error_compute_consumed in self.errored_txs_compute_consumed.drain(..) {
let compute_units_update =
std::cmp::max(current_estimated_program_cost, tx_error_compute_consumed);
self.accumulated_units = self.accumulated_units.saturating_add(compute_units_update);
self.count = self.count.saturating_add(1);
saturating_add_assign!(self.accumulated_units, compute_units_update);
saturating_add_assign!(self.count, 1);
}
}
pub fn accumulate_program_timings(&mut self, other: &ProgramTiming) {
self.accumulated_us = self.accumulated_us.saturating_add(other.accumulated_us);
self.accumulated_units = self
.accumulated_units
.saturating_add(other.accumulated_units);
self.count = self.count.saturating_add(other.count);
saturating_add_assign!(self.accumulated_us, other.accumulated_us);
saturating_add_assign!(self.accumulated_units, other.accumulated_units);
saturating_add_assign!(self.count, other.count);
// Clones the entire vector, maybe not great...
self.errored_txs_compute_consumed
.extend(other.errored_txs_compute_consumed.clone());
self.total_errored_units = self
.total_errored_units
.saturating_add(other.total_errored_units);
saturating_add_assign!(self.total_errored_units, 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 execute_us: u64,
pub deserialize_us: u64,
pub get_or_create_executor_us: u64,
pub changed_account_count: u64,
pub total_account_count: u64,
pub total_data_size: 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>,
}
impl ExecuteDetailsTimings {
pub fn accumulate(&mut self, other: &ExecuteDetailsTimings) {
self.serialize_us = self.serialize_us.saturating_add(other.serialize_us);
self.create_vm_us = self.create_vm_us.saturating_add(other.create_vm_us);
self.execute_us = self.execute_us.saturating_add(other.execute_us);
self.deserialize_us = self.deserialize_us.saturating_add(other.deserialize_us);
self.changed_account_count = self
.changed_account_count
.saturating_add(other.changed_account_count);
self.total_account_count = self
.total_account_count
.saturating_add(other.total_account_count);
self.total_data_size = self.total_data_size.saturating_add(other.total_data_size);
self.data_size_changed = self
.data_size_changed
.saturating_add(other.data_size_changed);
saturating_add_assign!(self.serialize_us, other.serialize_us);
saturating_add_assign!(self.create_vm_us, other.create_vm_us);
saturating_add_assign!(self.execute_us, other.execute_us);
saturating_add_assign!(self.deserialize_us, other.deserialize_us);
saturating_add_assign!(
self.get_or_create_executor_us,
other.get_or_create_executor_us
);
saturating_add_assign!(self.changed_account_count, other.changed_account_count);
saturating_add_assign!(self.total_account_count, other.total_account_count);
saturating_add_assign!(self.total_data_size, other.total_data_size);
saturating_add_assign!(self.data_size_changed, other.data_size_changed);
saturating_add_assign!(
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 {
let program_timing = self.per_program_timings.entry(*id).or_default();
program_timing.accumulate_program_timings(other);