Skip to content
This repository has been archived by the owner on Aug 21, 2024. It is now read-only.

Commit

Permalink
refactor: share benchmark code with simple flow test
Browse files Browse the repository at this point in the history
  • Loading branch information
avi-starkware committed May 23, 2024
1 parent e2fd958 commit 7d30fc1
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 113 deletions.
2 changes: 1 addition & 1 deletion crates/blockifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ num-traits.workspace = true
num-rational.workspace = true
once_cell.workspace = true
phf.workspace = true
rand.workspace = true
rstest = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["arbitrary_precision"] }
Expand All @@ -54,7 +55,6 @@ assert_matches.workspace = true
criterion = { workspace = true, features = ["html_reports"] }
glob.workspace = true
pretty_assertions.workspace = true
rand.workspace = true
regex.workspace = true
rstest.workspace = true
test-case.workspace = true
Expand Down
115 changes: 3 additions & 112 deletions crates/blockifier/bench/blockifier_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,128 +7,19 @@
//!
//! Run the benchmarks using `cargo bench --bench blockifier_bench`.

use blockifier::abi::abi_utils::selector_from_name;
use blockifier::blockifier::config::TransactionExecutorConfig;
use blockifier::blockifier::transaction_executor::TransactionExecutor;
use blockifier::bouncer::BouncerConfig;
use blockifier::context::{BlockContext, ChainInfo};
use blockifier::invoke_tx_args;
use blockifier::test_utils::contracts::FeatureContract;
use blockifier::test_utils::dict_state_reader::DictStateReader;
use blockifier::test_utils::initial_test_state::test_state;
use blockifier::test_utils::invoke::invoke_tx;
use blockifier::test_utils::{CairoVersion, NonceManager, BALANCE, MAX_FEE};
use blockifier::transaction::account_transaction::AccountTransaction;
use blockifier::transaction::constants::TRANSFER_ENTRY_POINT_NAME;
use blockifier::transaction::transaction_execution::Transaction;
use blockifier::test_utils::transfers_simulator::TransfersSimulator;
use criterion::{criterion_group, criterion_main, Criterion};
use rand::{Rng, SeedableRng};
use starknet_api::core::ContractAddress;
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::{Calldata, Fee, TransactionVersion};
use starknet_api::{calldata, stark_felt};

const N_ACCOUNTS: u16 = 10000;
const CHUNK_SIZE: usize = 10;
const RANDOMIZATION_SEED: u64 = 0;
const CHARGE_FEE: bool = false;
const TRANSACTION_VERSION: TransactionVersion = TransactionVersion(StarkFelt::ONE);

pub fn transfers_benchmark(c: &mut Criterion) {
let account_contract = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo0);
let block_context = BlockContext::create_for_account_testing();
let chain_info = &block_context.chain_info().clone();
let state = test_state(chain_info, BALANCE * 1000, &[(account_contract, N_ACCOUNTS)]);
// TODO(Avi, 20/05/2024): Enable concurrency.
let executor_config = TransactionExecutorConfig::default();
let executor =
&mut TransactionExecutor::new(state, block_context, BouncerConfig::max(), executor_config);
let accounts = (0..N_ACCOUNTS)
.map(|instance_id| account_contract.get_instance_address(instance_id))
.collect::<Vec<_>>();
let nonce_manager = &mut NonceManager::default();

let mut first_sender_index = 0;
let mut random_generator = rand::rngs::StdRng::seed_from_u64(RANDOMIZATION_SEED);
let mut transfers_simulator = TransfersSimulator::new();
// Create a benchmark group called "transfers", which iterates over the accounts round-robin
// and performs transfers.
c.bench_function("transfers", |benchmark| {
benchmark.iter(|| {
execute_chunk_of_transfers(
first_sender_index,
&mut random_generator,
&accounts,
nonce_manager,
chain_info,
executor,
);
first_sender_index = (first_sender_index + CHUNK_SIZE) % accounts.len();
transfers_simulator.execute_chunk_of_transfers();
})
});
}

fn execute_chunk_of_transfers(
first_sender_index: usize,
random_generator: &mut rand::rngs::StdRng,
accounts: &[ContractAddress],
nonce_manager: &mut NonceManager,
chain_info: &ChainInfo,
executor: &mut TransactionExecutor<DictStateReader>,
) {
let mut chunk: Vec<Transaction> = Vec::with_capacity(CHUNK_SIZE);
let mut sender_index = first_sender_index;
for _ in 0..CHUNK_SIZE {
let recipient_index = random_generator.gen::<usize>() % accounts.len();
let account_tx =
generate_transfer(accounts, sender_index, recipient_index, nonce_manager, chain_info);
chunk.push(Transaction::AccountTransaction(account_tx));
sender_index = (sender_index + 1) % accounts.len();
}
let results = executor.execute_txs(&chunk, CHARGE_FEE);
assert_eq!(results.len(), CHUNK_SIZE);
for result in results {
assert!(!result.unwrap().is_reverted());
}
// TODO(Avi, 01/06/2024): Run the same transactions concurrently on a new state and compare the
// state diffs.
}

fn generate_transfer(
accounts: &[ContractAddress],
sender_index: usize,
recipient_index: usize,
nonce_manager: &mut NonceManager,
chain_info: &ChainInfo,
) -> AccountTransaction {
let sender_address = accounts[sender_index];
let recipient_account_address = accounts[recipient_index];
let nonce = nonce_manager.next(sender_address);

let entry_point_selector = selector_from_name(TRANSFER_ENTRY_POINT_NAME);
let contract_address = match TRANSACTION_VERSION {
TransactionVersion::ONE => *chain_info.fee_token_addresses.eth_fee_token_address.0.key(),
TransactionVersion::THREE => *chain_info.fee_token_addresses.strk_fee_token_address.0.key(),
_ => panic!("Unsupported transaction version: {TRANSACTION_VERSION:?}"),
};

let execute_calldata = calldata![
contract_address, // Contract address.
entry_point_selector.0, // EP selector.
stark_felt!(3_u8), // Calldata length.
*recipient_account_address.0.key(), // Calldata: recipient.
stark_felt!(1_u8), // Calldata: lsb amount.
stark_felt!(0_u8) // Calldata: msb amount.
];

let tx = invoke_tx(invoke_tx_args! {
max_fee: Fee(MAX_FEE),
sender_address,
calldata: execute_calldata,
version: TRANSACTION_VERSION,
nonce,
});
AccountTransaction::Invoke(tx)
}

criterion_group!(benches, transfers_benchmark);
criterion_main!(benches);
2 changes: 2 additions & 0 deletions crates/blockifier/src/blockifier.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ pub mod block;
pub mod config;
pub mod stateful_validator;
pub mod transaction_executor;
#[cfg(test)]
pub mod transfers_flow_test;
10 changes: 10 additions & 0 deletions crates/blockifier/src/blockifier/transfers_flow_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use crate::test_utils::transfers_simulator::TransfersSimulator;

#[test]
pub fn transfers_flow_test() {
let n_chunks = 100;
let mut transfers_simulator = TransfersSimulator::new();
for _ in 0..n_chunks {
transfers_simulator.execute_chunk_of_transfers();
}
}
1 change: 1 addition & 0 deletions crates/blockifier/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod initial_test_state;
pub mod invoke;
pub mod prices;
pub mod struct_impls;
pub mod transfers_simulator;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
Expand Down
117 changes: 117 additions & 0 deletions crates/blockifier/src/test_utils/transfers_simulator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
use rand::{Rng, SeedableRng};
use starknet_api::core::ContractAddress;
use starknet_api::hash::StarkFelt;
use starknet_api::transaction::{Calldata, Fee, TransactionVersion};
use starknet_api::{calldata, stark_felt};

use crate::abi::abi_utils::selector_from_name;
use crate::blockifier::config::TransactionExecutorConfig;
use crate::blockifier::transaction_executor::TransactionExecutor;
use crate::bouncer::BouncerConfig;
use crate::context::{BlockContext, ChainInfo};
use crate::invoke_tx_args;
use crate::test_utils::contracts::FeatureContract;
use crate::test_utils::dict_state_reader::DictStateReader;
use crate::test_utils::initial_test_state::test_state;
use crate::test_utils::invoke::invoke_tx;
use crate::test_utils::{CairoVersion, NonceManager, BALANCE, MAX_FEE};
use crate::transaction::account_transaction::AccountTransaction;
use crate::transaction::constants::TRANSFER_ENTRY_POINT_NAME;
use crate::transaction::transaction_execution::Transaction;

const N_ACCOUNTS: u16 = 10000;
const CHUNK_SIZE: usize = 10;
const RANDOMIZATION_SEED: u64 = 0;
const CHARGE_FEE: bool = false;
const TRANSACTION_VERSION: TransactionVersion = TransactionVersion(StarkFelt::ONE);

pub struct TransfersSimulator {
accounts: Vec<ContractAddress>,
nonce_manager: NonceManager,
chain_info: ChainInfo,
executor: TransactionExecutor<DictStateReader>,
sender_index: usize,
recipient_generator: rand::rngs::StdRng,
}

impl Default for TransfersSimulator {
fn default() -> Self {
Self::new()
}
}

impl TransfersSimulator {
pub fn new() -> Self {
let account_contract = FeatureContract::AccountWithoutValidations(CairoVersion::Cairo0);
let block_context = BlockContext::create_for_account_testing();
let chain_info = &block_context.chain_info().clone();
let state = test_state(chain_info, BALANCE * 1000, &[(account_contract, N_ACCOUNTS)]);
// TODO(Avi, 20/05/2024): Enable concurrency.
let executor_config = TransactionExecutorConfig::default();
let executor =
TransactionExecutor::new(state, block_context, BouncerConfig::max(), executor_config);
let accounts = (0..N_ACCOUNTS)
.map(|instance_id| account_contract.get_instance_address(instance_id))
.collect::<Vec<_>>();
let nonce_manager = NonceManager::default();
let random_generator = rand::rngs::StdRng::seed_from_u64(RANDOMIZATION_SEED);
Self {
accounts,
nonce_manager,
chain_info: chain_info.clone(),
executor,
sender_index: 0,
recipient_generator: random_generator,
}
}

pub fn execute_chunk_of_transfers(&mut self) {
let mut chunk: Vec<Transaction> = Vec::with_capacity(CHUNK_SIZE);
for _ in 0..CHUNK_SIZE {
let account_tx = self.generate_transfer();
chunk.push(Transaction::AccountTransaction(account_tx));
}
let results = self.executor.execute_txs(&chunk, CHARGE_FEE);
assert_eq!(results.len(), CHUNK_SIZE);
for result in results {
assert!(!result.unwrap().is_reverted());
}
}

pub fn generate_transfer(&mut self) -> AccountTransaction {
let sender_address = self.accounts[self.sender_index];
self.sender_index = (self.sender_index + 1) % self.accounts.len();
let recipient_index = self.recipient_generator.gen::<usize>() % self.accounts.len();
let recipient_address = self.accounts[recipient_index];
let nonce = self.nonce_manager.next(sender_address);

let entry_point_selector = selector_from_name(TRANSFER_ENTRY_POINT_NAME);
let contract_address = match TRANSACTION_VERSION {
TransactionVersion::ONE => {
*self.chain_info.fee_token_addresses.eth_fee_token_address.0.key()
}
TransactionVersion::THREE => {
*self.chain_info.fee_token_addresses.strk_fee_token_address.0.key()
}
_ => panic!("Unsupported transaction version: {TRANSACTION_VERSION:?}"),
};

let execute_calldata = calldata![
contract_address, // Contract address.
entry_point_selector.0, // EP selector.
stark_felt!(3_u8), // Calldata length.
*recipient_address.0.key(), // Calldata: recipient.
stark_felt!(1_u8), // Calldata: lsb amount.
stark_felt!(0_u8) // Calldata: msb amount.
];

let tx = invoke_tx(invoke_tx_args! {
max_fee: Fee(MAX_FEE),
sender_address,
calldata: execute_calldata,
version: TRANSACTION_VERSION,
nonce,
});
AccountTransaction::Invoke(tx)
}
}

0 comments on commit 7d30fc1

Please sign in to comment.