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

refactor: share benchmark code with simple flow test #1917

Merged
merged 1 commit into from
Jun 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
}
}
Loading