8000 fix(sequencer): allow benchmarks to run on macOS and fix issues by Fraser999 · Pull Request #1842 · astriaorg/astria · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

fix(sequencer): allow benchmarks to run on macOS and fix issues #1842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 16, 2025
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
11 changes: 9 additions & 2 deletions crates/astria-sequencer/benches/benchmark.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
// Required to force the benchmark target to actually register the divan benchmark cases.
use astria_sequencer as _;
//! To run all sequencer benchmarks, from the root of the monorepo, run:
//! ```sh
//! cargo bench --features=benchmark -qp astria-sequencer
//! ```

fn main() {
// Required to force the benchmark target to actually register the divan benchmark cases.
// See https://github.com/nvzqz/divan/issues/61#issuecomment-2500002168.
use config::Config as _;
let _ = astria_sequencer::Config::get();

// Handle `nextest` querying the benchmark binary for tests. Currently `divan` is incompatible
// with `nextest`, so just report no tests available.
// See https://github.com/nvzqz/divan/issues/43 for further details.
Expand Down
101 changes: 28 additions & 73 deletions crates/astria-sequencer/src/app/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,31 +5,18 @@

use std::time::Duration;

use astria_core::{
protocol::genesis::v1::{
Account,
GenesisAppState,
},
Protobuf,
};
use cnidarium::Storage;

use crate::{
app::{
benchmark_and_test_utils::{
mock_balances,
mock_tx_cost,
AppInitializer,
},
App,
},
benchmark_and_test_utils::astria_address,
benchmark_utils::{
self,
new_fixture,
TxTypes,
SIGNER_COUNT,
},
proposal::block_size_constraints::BlockSizeConstraints,
test_utils::{
dummy_balances,
dummy_tx_costs,
Fixture,
},
};

/// The max time for any benchmark.
Expand All @@ -40,69 +27,37 @@ const MAX_TIME: Duration = Duration::from_secs(120);
/// `prepare_proposal` during stress testing using spamoor.
const COMETBFT_MAX_TX_BYTES: i64 = 22_019_254;

struct Fixture {
app: App,
_storage: Storage,
}

impl Fixture {
/// Initializes a new `App` instance with the genesis accounts derived from the secret keys of
/// `benchmark_utils::signing_keys()`, and inserts transactions into the app mempool.
async fn new() -> Fixture {
let accounts = benchmark_utils::signing_keys()
.enumerate()
.take(usize::from(SIGNER_COUNT))
.map(|(index, signing_key)| Account {
address: astria_address(&signing_key.address_bytes()),
balance: 10u128
.pow(19)
.saturating_add(u128::try_from(index).unwrap()),
})
.map(Protobuf::into_raw)
.collect::<Vec<_>>();
let first_address = accounts.first().cloned().unwrap().address;
let genesis_state = GenesisAppState::try_from_raw(
astria_core::generated::astria::protocol::genesis::v1::GenesisAppState {
accounts,
authority_sudo_address: first_address.clone(),
ibc_sudo_address: first_address.clone(),
..crate::app::benchmark_and_test_utils::proto_genesis_state()
},
)
.unwrap();

let (app, storage) = AppInitializer::new()
.with_genesis_state(genesis_state)
.init()
.await;

let mock_balances = mock_balances(0, 0);
let mock_tx_cost = mock_tx_cost(0, 0, 0);

for tx in benchmark_utils::transactions(TxTypes::AllTransfers) {
app.mempool
.insert(tx.clone(), 0, &mock_balances.clone(), mock_tx_cost.clone())
/// Initializes a new `App` instance with the genesis accounts derived from the secret keys of
/// `benchmark_utils::signing_keys()`, and inserts transactions into the app mempool.
fn initialize() -> Fixture {
let dummy_balances = dummy_balances(0, 0);
let dummy_tx_costs = dummy_tx_costs(0, 0, 0);
let txs = benchmark_utils::transactions(TxTypes::AllTransfers);
let fixture = new_fixture();
let mempool = fixture.mempool();
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();
runtime.block_on(async {
for tx in txs {
mempool
.insert(tx.clone(), 0, &dummy_balances, dummy_tx_costs.clone())
.await
.unwrap();
}
Fixture {
app,
_storage: storage,
}
}
});

fixture
}

#[divan::bench(max_time = MAX_TIME)]
fn prepare_proposal_tx_execution(bencher: divan::Bencher) {
let runtime = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.unwrap();
let mut fixture = runtime.block_on(async { Fixture::new().await });
let runtime = tokio::runtime::Builder::new_multi_thread().build().unwrap();
let mut fixture = initialize();
bencher
.with_inputs(|| BlockSizeConstraints::new(COMETBFT_MAX_TX_BYTES, true).unwrap())
.bench_local_refs(|constraints| {
let (_tx_bytes, included_txs) = runtime.block_on(async {
let executed_txs = runtime.block_on(async {
fixture
.app
.prepare_proposal_tx_execution(*constraints)
Expand All @@ -111,6 +66,6 @@ fn prepare_proposal_tx_execution(bencher: divan::Bencher) {
});
// Ensure we actually processed some txs. This will trip if execution fails for all
// txs, or more likely, if the mempool becomes exhausted of txs.
assert!(!included_txs.is_empty());
assert!(!executed_txs.is_empty());
});
}
4 changes: 2 additions & 2 deletions crates/astria-sequencer/src/app/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// #[cfg(feature = "benchmark")]
// mod benchmarks;
#[cfg(feature = "benchmark")]
mod benchmarks;
pub(crate) mod event_bus;
mod execution_state;
mod state_ext;
Expand Down
161 changes: 93 additions & 68 deletions crates/astria-sequencer/src/benchmark_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,39 +8,31 @@ use std::{

use astria_core::{
crypto::SigningKey,
primitive::v1::{
asset::{
Denom,
IbcPrefixed,
},
RollupId,
},
protocol::transaction::v1::{
action,
action::Action,
Transaction,
TransactionBody,
},
protocol::transaction::v1::action::Transfer,
};

use crate::test_utils::{
astria_address,
nria,
use crate::{
checked_transaction::CheckedTransaction,
test_utils::{
astria_address,
nria,
Fixture,
},
};

/// The number of different signers of transactions, and also the number of different chain IDs.
/// The number of different signers of transactions.
pub(crate) const SIGNER_COUNT: u8 = 10;
/// The number of transfers per transaction.
///
/// 2866 chosen after experimentation of spamming composer.
pub(crate) const TRANSFERS_PER_TX: usize = 2866;

const SEQUENCE_ACTION_TX_COUNT: usize = 100_001;
const TRANSFERS_TX_COUNT: usize = 10_000;
const ROLLUP_DATA_TX_COUNT: usize = 100_001;
const TRANSFERS_TX_COUNT: usize = 1_000;

#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
pub(crate) enum TxTypes {
AllSequenceActions,
AllRollupDataSubmissions,
AllTransfers,
}

Expand All @@ -58,75 +50,108 @@ pub(crate) fn signing_keys() -> impl Iterator<Item = &'static SigningKey> {
}

/// Returns a static ref to a collection of `MAX_INITIAL_TXS + 1` transactions.
pub(crate) fn transactions(tx_types: TxTypes) -> &'static Vec<Arc<Transaction>> {
static TXS: OnceLock<HashMap<TxTypes, Vec<Arc<Transaction>>>> = OnceLock::new();
pub(crate) fn transactions(tx_types: TxTypes) -> &'static Vec<Arc<CheckedTransaction>> {
static TXS: OnceLock<HashMap<TxTypes, Vec<Arc<CheckedTransaction>>>> = OnceLock::new();
TXS.get_or_init(|| {
let fixture = new_fixture();
let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();

let mut map = HashMap::new();
map.insert(
TxTypes::AllSequenceActions,
rollup_data_submission_actions(),
TxTypes::AllRollupDataSubmissions,
runtime.block_on(async { rollup_data_submission_actions(&fixture).await }),
);
map.insert(
TxTypes::AllTransfers,
runtime.block_on(async { transfers(&fixture).await }),
);
map.insert(TxTypes::AllTransfers, transfers());
map
})
.get(&tx_types)
.unwrap()
}

/// Returns a new test [`Fixture`] where all accounts under [`signing_keys`] have been funded at
/// genesis, and where the first of these is used as the sudo address and IBC sudo address.
pub(crate) fn new_fixture() -> Fixture {
let accounts = signing_keys()
.enumerate()
.take(usize::from(SIGNER_COUNT))
.map(|(index, signing_key)| {
let address = astria_address(&signing_key.address_bytes());
let balance = 10_u128
.pow(19)
.saturating_add(u128::try_from(index).unwrap());
(address, balance)
});

let first_address = astria_address(&signing_keys().next().unwrap().address_bytes());

let runtime = tokio::runtime::Builder::new_current_thread()
.build()
.unwrap();

runtime.block_on(async {
let mut fixture = Fixture::uninitialized(None).await;
fixture
.chain_initializer()
.with_genesis_accounts(accounts)
.with_authority_sudo_address(first_address)
.with_ibc_sudo_address(first_address)
.init()
.await;
fixture
})
}

#[expect(
clippy::mutable_key_type,
reason = "false-positive as described in \"Known problems\" of lint"
)]
fn rollup_data_submission_actions() -> Vec<Arc<Transaction>> {
let mut nonces_and_chain_ids = HashMap::new();
signing_keys()
.map(move |signing_key| {
let verification_key = signing_key.verification_key();
let (nonce, chain_id) = nonces_and_chain_ids
.entry(verification_key)
.or_insert_with(|| (0_u32, format!("chain-{}", signing_key.verification_key())));
let action = action::RollupDataSubmission {
rollup_id: RollupId::new([1; 32]),
data: vec![2; 1000].into(),
fee_asset: Denom::IbcPrefixed(IbcPrefixed::new([3; 32])),
};
let tx = TransactionBody::builder()
.actions(vec![Action::RollupDataSubmission(action)])
.nonce(*nonce)
.chain_id(chain_id.as_str())
.try_build()
.expect("failed to build transaction from actions")
.sign(signing_key);
Arc::new(tx)
})
.take(SEQUENCE_ACTION_TX_COUNT)
.collect()
async fn rollup_data_submission_actions(fixture: &Fixture) -> Vec<Arc<CheckedTransaction>> {
let mut nonces = HashMap::new();
let mut txs = Vec::with_capacity(ROLLUP_DATA_TX_COUNT);
for signing_key in signing_keys().take(ROLLUP_DATA_TX_COUNT) {
let nonce = nonces
.entry(signing_key.verification_key())
.or_insert(0_u32);
let tx = fixture
.checked_tx_builder()
.with_rollup_data_submission(vec![2; 1000])
.with_signer(signing_key.clone())
.with_nonce(*nonce)
.build()
.await;
txs.push(tx);
*nonce = (*nonce).wrapping_add(1);
}
txs
}

fn transfers() -> Vec<Arc<Transaction>> {
async fn transfers(fixture: &Fixture) -> Vec<Arc<CheckedTransaction>> {
let sender = signing_keys().next().unwrap();
let receiver = signing_keys().nth(1).unwrap();
let to = astria_address(&receiver.address_bytes());
let action = Action::from(action::Transfer {
let transfer = Transfer {
to,
amount: 1,
asset: nria().into(),
fee_asset: nria().into(),
});
(0..TRANSFERS_TX_COUNT)
.map(|nonce| {
let tx = TransactionBody::builder()
.actions(
std::iter::repeat(action.clone())
.take(TRANSFERS_PER_TX)
.collect(),
)
.nonce(u32::try_from(nonce).unwrap())
.chain_id("test")
.try_build()
.expect("failed to build transaction from actions")
.sign(sender);
Arc::new(tx)
})
.collect()
};
let mut txs = Vec::with_capacity(TRANSFERS_TX_COUNT);
for nonce in 0..TRANSFERS_TX_COUNT {
let mut tx_builder = fixture.checked_tx_builder();
for _ in 0..TRANSFERS_PER_TX {
tx_builder = tx_builder.with_action(transfer.clone());
}
let tx = tx_builder
.with_nonce(u32::try_from(nonce).unwrap())
.with_signer(sender.clone())
.build()
.await;
txs.push(tx);
}
txs
}
Loading
Loading
0