Skip to content
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

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
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
10 changes: 4 additions & 6 deletions crates/astria-sequencer/src/app/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ use cnidarium::Storage;
use crate::{
app::{
benchmark_and_test_utils::{
initialize_app_with_storage,
mock_balances,
mock_tx_cost,
proto_genesis_state,
},
App,
},
Expand Down Expand Up @@ -65,16 +67,12 @@ impl Fixture {
accounts,
authority_sudo_address: first_address.clone(),
ibc_sudo_address: first_address.clone(),
..crate::app::benchmark_and_test_utils::proto_genesis_state()
..proto_genesis_state()
},
)
.unwrap();

let (app, storage) = crate::app::benchmark_and_test_utils::initialize_app_with_storage(
Some(genesis_state),
vec![],
)
.await;
let (app, storage) = initialize_app_with_storage(Some(genesis_state), vec![]).await;

let mock_balances = mock_balances(0, 0);
let mock_tx_cost = mock_tx_cost(0, 0, 0);
Expand Down
1 change: 1 addition & 0 deletions crates/astria-sequencer/src/benchmark_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ fn rollup_data_submission_actions() -> Vec<Arc<Transaction>> {
.try_build()
.expect("failed to build transaction from actions")
.sign(signing_key);
*nonce = (*nonce).wrapping_add(1);
Arc::new(tx)
})
.take(SEQUENCE_ACTION_TX_COUNT)
Expand Down
89 changes: 63 additions & 26 deletions crates/astria-sequencer/src/mempool/benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,36 +100,50 @@ fn transactions() -> &'static Vec<Arc<Transaction>> {
/// Returns a new `Mempool` initialized with the number of transactions specified by `T::size()`
/// taken from the static `transactions()`, and with a full `comet_bft_removal_cache`.
fn init_mempool<T: MempoolSize>() -> Mempool {
static CELL_100: std::sync::OnceLock<Mempool> = std::sync::OnceLock::new();
static CELL_1_000: std::sync::OnceLock<Mempool> = std::sync::OnceLock::new();
static CELL_10_000: std::sync::OnceLock<Mempool> = std::sync::OnceLock::new();
static CELL_100_000: std::sync::OnceLock<Mempool> = std::sync::OnceLock::new();
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.unwrap();
let metrics = Box::leak(Box::new(Metrics::noop_metrics(&()).unwrap()));
let mempool = Mempool::new(metrics, T::size());
let account_mock_balance = mock_balances(0, 0);
let tx_mock_cost = mock_tx_cost(0, 0, 0);
runtime.block_on(async {
for tx in transactions().iter().take(T::checked_size()) {
mempool
.insert(
tx.clone(),
0,
account_mock_balance.clone(),
tx_mock_cost.clone(),
)
.await
.unwrap();
}
for i in 0..super::REMOVAL_CACHE_SIZE {
let hash = Sha256::digest(i.to_le_bytes()).into();
mempool
.comet_bft_removal_cache
.write()
.await
.add(hash, RemovalReason::Expired);
}
});
mempool
let init = || {
let metrics = Box::leak(Box::new(Metrics::noop_metrics(&()).unwrap()));
let mempool = Mempool::new(metrics, T::size());
runtime.block_on(async {
let account_mock_balance = mock_balances(0, 0);
let tx_mock_cost = mock_tx_cost(0, 0, 0);
for tx in transactions().iter().take(T::checked_size()) {
mempool
.insert(
tx.clone(),
0,
account_mock_balance.clone(),
tx_mock_cost.clone(),
)
.await
.unwrap();
}
for i in 0..super::REMOVAL_CACHE_SIZE {
let hash = Sha256::digest(i.to_le_bytes()).into();
mempool
.comet_bft_removal_cache
.write()
.await
.add(hash, RemovalReason::Expired);
}
});
mempool
};
let mempool = match T::checked_size() {
100 => CELL_100.get_or_init(init),
1_000 => CELL_1_000.get_or_init(init),
10_000 => CELL_10_000.get_or_init(init),
100_000 => CELL_100_000.get_or_init(init),
_ => unreachable!(),
};
runtime.block_on(async { mempool.deep_clone().await })
}

/// Returns the first transaction from the static `transactions()` not included in the initialized
Expand All @@ -138,6 +152,29 @@ fn get_unused_tx<T: MempoolSize>() -> Arc<Transaction> {
transactions().get(T::checked_size()).unwrap().clone()
}

/// This is not really a benchmark test, rather a means to memoize the data used in the "real"
/// benchmark tests below.
///
/// It should always be named so that it is alphabetically first in the suite of tests, since the
/// tests are run in that order.
///
/// This means that all the real tests are able to have a meaningful number of iterations, making
/// the results more accurate, rather than one test only having a single iteration due to the time
/// taken to memoize the data.
#[divan::bench(
max_time = MAX_TIME,
types = [
mempool_with_100_txs,
mempool_with_1000_txs,
mempool_with_10000_txs,
mempool_with_100000_txs
]
)]
fn a_warmup<T: MempoolSize>(bencher: divan::Bencher) {
init_mempool::<T>();
bencher.bench(|| ());
}

/// Benchmarks `Mempool::insert` for a single new transaction on a mempool with the given number of
/// existing entries.
#[divan::bench(
Expand Down
13 changes: 13 additions & 0 deletions crates/astria-sequencer/src/mempool/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,19 @@ impl Mempool {
let parked = self.parked.write().await;
(pending, parked)
}

#[cfg(feature = "benchmark")]
pub(crate) async fn deep_clone(&self) -> Self {
Mempool {
pending: Arc::new(RwLock::new(self.pending.read().await.clone())),
parked: Arc::new(RwLock::new(self.parked.read().await.clone())),
comet_bft_removal_cache: Arc::new(RwLock::new(
self.comet_bft_removal_cache.read().await.clone(),
)),
contained_txs: Arc::new(RwLock::new(self.contained_txs.read().await.clone())),
metrics: self.metrics,
}
}
}

#[cfg(test)]
Expand Down
31 changes: 5 additions & 26 deletions crates/astria-sequencer/src/mempool/transactions_container.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,10 +106,6 @@ impl TimemarkedTransaction {
&self.address
}

pub(super) fn cost(&self) -> &HashMap<IbcPrefixed, u128> {
&self.cost
}

pub(super) fn id(&self) -> [u8; 32] {
self.tx_hash
}
Expand Down Expand Up @@ -217,28 +213,11 @@ impl PendingTransactionsForAccount {
) -> Vec<TimemarkedTransaction> {
let mut split_at = 0;

'outer: for (nonce, tx) in &self.txs {
for (nonce, tx) in &self.txs {
// ensure we have enough balance to cover inclusion
for (denom, cost) in tx.cost() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice refactor 👍

if *cost == 0 {
continue;
}
match available_balances.entry(*denom) {
hash_map::Entry::Occupied(mut entry) => {
// try to subtract cost, if not enough balance, do not include
let current_balance = entry.get_mut();
*current_balance = match current_balance.checked_sub(*cost) {
None => break 'outer,
Some(new_value) => new_value,
};
}
hash_map::Entry::Vacant(_) => {
// not enough balance, do not include
break 'outer;
}
}
if tx.deduct_costs(&mut available_balances).is_err() {
break;
}

split_at = nonce.saturating_add(1);
}

Expand Down Expand Up @@ -1712,7 +1691,7 @@ mod tests {
.txs
.get(&0)
.unwrap()
.cost()
.cost
.get(&denom_0().to_ibc_prefixed())
.unwrap(),
&0,
Expand All @@ -1737,7 +1716,7 @@ mod tests {
.txs
.get(&0)
.unwrap()
.cost()
.cost
.get(&denom_0().to_ibc_prefixed())
.unwrap(),
&MOCK_SEQUENCE_FEE,
Expand Down
Loading