Skip to content

Commit

Permalink
Implement basic bench block range
Browse files Browse the repository at this point in the history
  • Loading branch information
JulianGCalderon committed Jul 10, 2024
1 parent 6a68b7c commit df53103
Showing 1 changed file with 84 additions and 129 deletions.
213 changes: 84 additions & 129 deletions replay/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::str::FromStr;

use blockifier::state::cached_state::CachedState;
use clap::{Parser, Subcommand};
use rpc_state_reader::{
Expand All @@ -7,35 +9,14 @@ use rpc_state_reader::{
};

use rpc_state_reader::blockifier_state_reader::execute_tx_configurable;
#[cfg(feature = "benchmark")]
use rpc_state_reader::{
execute_tx_configurable_with_state, rpc_state::RpcBlockInfo, RpcStateReader,
};
use starknet_api::block::BlockNumber;
#[cfg(feature = "benchmark")]
use starknet_api::{
hash::StarkFelt,
stark_felt,
transaction::{Transaction, TransactionHash},
};
#[cfg(feature = "benchmark")]
use starknet_in_rust::{
definitions::block_context::GasPrices,
state::{
cached_state::CachedState, contract_class_cache::PermanentContractClassCache, BlockInfo,
},
transaction::Address,
Felt252,
};
#[cfg(feature = "benchmark")]
use std::ops::Div;
use std::str::FromStr;
#[cfg(feature = "benchmark")]
use std::{collections::HashMap, sync::Arc, time::Instant};
use tracing::{debug, error, info, info_span};
use tracing_subscriber::filter::Directive;
use tracing_subscriber::{util::SubscriberInitExt, EnvFilter};

#[cfg(feature = "benchmark")]
use std::{ops::Div, time::Instant};

#[derive(Debug, Parser)]
#[command(about = "Replay is a tool for executing Starknet transactions.", long_about = None)]
struct ReplayCLI {
Expand Down Expand Up @@ -68,7 +49,7 @@ Caches all rpc data before the benchmark runs to provide accurate results"
block_start: u64,
block_end: u64,
chain: String,
n_runs: usize,
number_of_runs: usize,
},
}

Expand Down Expand Up @@ -124,125 +105,99 @@ fn main() {
block_start,
block_end,
chain,
n_runs,
number_of_runs,
} => {
println!("Filling up Cache");
let network = parse_network(&chain);
// Create a single class_cache for all states
let class_cache = Arc::new(PermanentContractClassCache::default());
// HashMaps to cache tx data & states
let mut transactions =
HashMap::<BlockNumber, Vec<(TransactionHash, Transaction)>>::new();
let mut cached_states = HashMap::<
BlockNumber,
CachedState<RpcStateReader, PermanentContractClassCache>,
>::new();
let mut block_timestamps = HashMap::<BlockNumber, u64>::new();
let mut sequencer_addresses = HashMap::<BlockNumber, Address>::new();
let mut gas_prices = HashMap::<BlockNumber, GasPrices>::new();
info!("filling up cache");

let mut block_caches = Vec::new();

for block_number in block_start..=block_end {
// For each block:
let block_number = BlockNumber(block_number);
// For each block

// Create a cached state
let rpc_reader =
RpcStateReader::new(RpcState::new_rpc(network, block_number.into()).unwrap());
let mut state = CachedState::new(Arc::new(rpc_reader), class_cache.clone());
// Fetch block timestamps & sequencer address
let RpcBlockInfo {
block_timestamp,
sequencer_address,
..
} = state.state_reader.0.get_block_info().unwrap();
block_timestamps.insert(block_number, block_timestamp.0);
let sequencer_address = Address(Felt252::from_bytes_be_slice(
sequencer_address.0.key().bytes(),
));
sequencer_addresses.insert(block_number, sequencer_address.clone());
// Fetch gas price
let gas_price = state.state_reader.0.get_gas_price(block_number.0).unwrap();
gas_prices.insert(block_number, gas_price.clone());

// Fetch txs for the block
let transaction_hashes = get_transaction_hashes(block_number, network)
.expect("Unable to fetch the transaction hashes.");
let mut txs_in_block = Vec::<(TransactionHash, Transaction)>::new();
for tx_hash in transaction_hashes {
// Fetch tx and add it to txs_in_block cache
let tx_hash = TransactionHash(stark_felt!(tx_hash.strip_prefix("0x").unwrap()));
let tx = state.state_reader.0.get_transaction(&tx_hash).unwrap();
txs_in_block.push((tx_hash, tx.clone()));
// First execution to fill up cache values
let _ = execute_tx_configurable_with_state(
&tx_hash,
tx.clone(),
network,
BlockInfo {
block_number: block_number.0,
block_timestamp: block_timestamp.0,
gas_price: gas_price.clone(),
sequencer_address: sequencer_address.clone(),
},
false,
true,
let mut state = build_cached_state(&chain, block_number - 1);

// Fetch transactions for the block
let transaction_hashes = get_transaction_hashes(&chain, block_number).unwrap();

for transaction_hash in &transaction_hashes {
// Execute each transaction

// Internally, this fetches all the needed information and caches it
let result = execute_tx_configurable(
&mut state,
&transaction_hash,
BlockNumber(block_number),
false,
false,
);

match result {
Ok((info, ..)) => {
info!(
transaction_hash,
succeeded = info.revert_error.is_none(),
"tx execution status"
)
}
Err(_) => error!(transaction_hash, "tx execution failed"),
}
}
// Add the txs from the current block to the transactions cache
transactions.insert(block_number, txs_in_block);
// Clean writes from cached_state
state.cache_mut().storage_writes_mut().clear();
state.cache_mut().class_hash_writes_mut().clear();
state.cache_mut().nonce_writes_mut().clear();
// Add the cached state for the current block to the cached_states cache
cached_states.insert(block_number, state);

// todo: clear cache, is not public
// state.storage_writes_mut().clear();
// state.class_hash_writes_mut().clear();
// state.nonce_writes_mut().clear();

block_caches.push((block_number, state, transaction_hashes));
}

let before_execution = Instant::now();

info!("replaying with cached state");
// Benchmark run should make no api requests as all data is cached
// It shouldn't compile contracts with native either

println!(
"Executing block range: {} - {} {} times",
block_start, block_end, n_runs
);
let now = Instant::now();
for _ in 0..n_runs {
for block_number in block_start..=block_end {
let block_number = BlockNumber(block_number);
// Fetch state
let state = cached_states.get_mut(&block_number).unwrap();
// Fetch txs
let block_txs = transactions.get(&block_number).unwrap();
// Fetch timestamp
let block_timestamp = *block_timestamps.get(&block_number).unwrap();
// Fetch sequencer address
let sequencer_address = sequencer_addresses.get(&block_number).unwrap();
// Fetch gas price
let gas_price = gas_prices.get(&block_number).unwrap();
// Run txs
for (tx_hash, tx) in block_txs {
let _ = execute_tx_configurable_with_state(
tx_hash,
tx.clone(),
network,
BlockInfo {
block_number: block_number.0,
block_timestamp,
gas_price: gas_price.clone(),
sequencer_address: sequencer_address.clone(),
},
false,
true,
for _ in 0..number_of_runs {
for (block_number, state, transactions) in &mut block_caches {
for transaction_hash in transactions {
// Execute each transaction

// We use the same state from the previous execution
let result = execute_tx_configurable(
state,
&transaction_hash,
BlockNumber(*block_number),
false,
false,
);

match result {
Ok((info, ..)) => {
info!(
transaction_hash,
succeeded = info.revert_error.is_none(),
"tx execution status"
)
}
Err(_) => error!(transaction_hash, "tx execution failed"),
}
}

// todo: clear cache, is not public
// state.storage_writes_mut().clear();
// state.class_hash_writes_mut().clear();
// state.nonce_writes_mut().clear();
}
}
let elapsed_time = now.elapsed();
println!(
"Ran blocks {} - {} {} times in {} seconds. Approximately {} second(s) per run",

let execution_time = before_execution.elapsed();

let total_run_time = execution_time.as_secs_f64();
let average_run_time = total_run_time.div(number_of_runs as f64);
info!(
block_start,
block_end,
n_runs,
elapsed_time.as_secs_f64(),
elapsed_time.as_secs_f64().div(n_runs as f64)
block_end, number_of_runs, total_run_time, average_run_time, "benchmark finished",
);
}
}
Expand Down

0 comments on commit df53103

Please sign in to comment.