use chrono::Utc;
use litesvm::LiteSVM;
use log::info;
use solana_message::Message;
use solana_transaction::Transaction;
use super::context::{
discover_instruction_context, discover_transaction_context, TransactionExecutionContext,
};
use super::estimate::{ComputeUnitStats, InstructionBenchmarkResult, StatType};
use crate::cu_bench::{InstructionBenchmark, TransactionBenchmark};
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct TransactionBenchmarkResult {
pub transaction_name: String,
pub cu_estimate: ComputeUnitStats,
pub execution_context: TransactionExecutionContext,
pub generated_at: String,
pub generated_by: String,
}
pub fn benchmark_instruction<T: InstructionBenchmark>(
benchmark: T,
samples: usize,
) -> InstructionBenchmarkResult {
let mut svm = benchmark.setup_svm();
let execution_context = discover_instruction_context(&benchmark, &mut svm);
let mut cu_measurements = Vec::new();
for i in 0..samples {
let cu_used = measure_instruction(&benchmark, &mut svm);
cu_measurements.push(cu_used);
if (i + 1) % 10 == 0 {
info!("Completed {} measurements...", i + 1);
}
}
InstructionBenchmarkResult {
instruction_name: benchmark.instruction_name().to_string(),
cu_estimate: ComputeUnitStats::from_measurements(
StatType::Instruction(benchmark.instruction_name().to_string()),
&cu_measurements,
),
execution_context,
generated_at: Utc::now().to_rfc3339(),
generated_by: generated_by(),
}
}
pub fn benchmark_transaction<T: TransactionBenchmark>(
mut benchmark: T,
samples: usize,
) -> TransactionBenchmarkResult {
let mut svm = benchmark.setup_svm();
let context_tx = benchmark.build_transaction(&mut svm);
let workflow_name = benchmark.transaction_name().to_string();
let address_book = benchmark.address_book();
let execution_context =
discover_transaction_context(&context_tx, workflow_name, &mut svm, &address_book);
let mut cu_measurements = Vec::new();
for i in 0..samples {
let tx = benchmark.build_transaction(&mut svm);
let cu_used = measure_transaction_cu(&tx, &mut svm);
cu_measurements.push(cu_used);
if (i + 1) % 10 == 0 {
info!("Completed {} measurements...", i + 1);
}
}
TransactionBenchmarkResult {
transaction_name: benchmark.transaction_name().to_string(),
cu_estimate: ComputeUnitStats::from_measurements(
StatType::Transaction(benchmark.transaction_name().to_string()),
&cu_measurements,
),
execution_context,
generated_at: Utc::now().to_rfc3339(),
generated_by: generated_by(),
}
}
fn measure_transaction_cu(transaction: &Transaction, svm: &mut LiteSVM) -> u64 {
let result = svm.send_transaction(transaction.clone()).unwrap();
result.compute_units_consumed
}
fn measure_instruction<T: InstructionBenchmark>(benchmark: &T, svm: &mut LiteSVM) -> u64 {
let (target_ix, signer_pubkeys) = benchmark.build_instruction(svm);
svm.expire_blockhash();
let message = Message::new(&[target_ix], Some(&signer_pubkeys[0]));
let mut unsigned_tx = Transaction::new_unsigned(message);
unsigned_tx.message.recent_blockhash = svm.latest_blockhash();
let signed_tx = benchmark.sign_transaction(unsigned_tx);
let result = svm.send_transaction(signed_tx).unwrap();
result.compute_units_consumed
}
fn generated_by() -> String {
format!("{}@{}", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"))
}