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 (#1917)
Browse files Browse the repository at this point in the history
  • Loading branch information
avi-starkware authored Jun 5, 2024
1 parent 0abafd7 commit e2cfead
Show file tree
Hide file tree
Showing 6 changed files with 138 additions and 124 deletions.
3 changes: 2 additions & 1 deletion crates/blockifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ description = "The transaction-executing component in the Starknet sequencer."
workspace = true

[features]
testing = ["rstest"]
testing = ["rand", "rstest"]
concurrency = []

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down Expand Up @@ -39,6 +39,7 @@ num-traits.workspace = true
num-rational.workspace = true
once_cell.workspace = true
phf.workspace = true
rand = { workspace = true, optional = true }
rstest = { workspace = true, optional = true }
serde = { workspace = true, features = ["derive"] }
serde_json = { workspace = true, features = ["arbitrary_precision"] }
Expand Down
126 changes: 3 additions & 123 deletions crates/blockifier/bench/blockifier_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,139 +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::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_generator::TransfersGenerator;
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 mut transfers_simulator = TransfersGenerator::new();
let mut transfers_generator = TransfersGenerator::new();
// Create a benchmark group called "transfers", which iterates over the accounts round-robin
// and performs transfers.
c.bench_function("transfers", |benchmark| {
benchmark.iter(|| {
transfers_simulator.execute_chunk_of_transfers();
transfers_generator.execute_chunk_of_transfers();
})
});
}
pub struct TransfersGenerator {
account_addresses: Vec<ContractAddress>,
chain_info: ChainInfo,
executor: TransactionExecutor<DictStateReader>,
nonce_manager: NonceManager,
recipient_generator: rand::rngs::StdRng,
sender_index: usize,
}

impl TransfersGenerator {
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, executor_config);
let account_addresses = (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 {
account_addresses,
nonce_manager,
chain_info,
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 sender_address = self.account_addresses[self.sender_index];
self.sender_index = (self.sender_index + 1) % self.account_addresses.len();
let recipient_index =
self.recipient_generator.gen::<usize>() % self.account_addresses.len();
let recipient_address = self.account_addresses[recipient_index];

let account_tx = self.generate_transfer(sender_address, recipient_address);
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());
}
// TODO(Avi, 01/06/2024): Run the same transactions concurrently on a new state and compare
// the state diffs.
}

pub fn generate_transfer(
&mut self,
sender_address: ContractAddress,
recipient_address: ContractAddress,
) -> AccountTransaction {
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)
}
}

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

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;
7 changes: 7 additions & 0 deletions crates/blockifier/src/blockifier/transfers_flow_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
use crate::test_utils::transfers_generator::TransfersGenerator;

#[test]
pub fn transfers_flow_test() {
let mut transfers_generator = TransfersGenerator::new();
transfers_generator.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_generator;
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
Expand Down
123 changes: 123 additions & 0 deletions crates/blockifier/src/test_utils/transfers_generator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
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::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 TransfersGenerator {
account_addresses: Vec<ContractAddress>,
chain_info: ChainInfo,
executor: TransactionExecutor<DictStateReader>,
nonce_manager: NonceManager,
recipient_generator: rand::rngs::StdRng,
sender_index: usize,
}

impl TransfersGenerator {
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, executor_config);
let account_addresses = (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 {
account_addresses,
nonce_manager,
chain_info,
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 sender_address = self.account_addresses[self.sender_index];
self.sender_index = (self.sender_index + 1) % self.account_addresses.len();
let recipient_index =
self.recipient_generator.gen::<usize>() % self.account_addresses.len();
let recipient_address = self.account_addresses[recipient_index];

let account_tx = self.generate_transfer(sender_address, recipient_address);
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());
}
// TODO(Avi, 01/06/2024): Run the same transactions concurrently on a new state and compare
// the state diffs.
}

pub fn generate_transfer(
&mut self,
sender_address: ContractAddress,
recipient_address: ContractAddress,
) -> AccountTransaction {
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)
}
}

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

0 comments on commit e2cfead

Please sign in to comment.