Skip to content

Commit 9d988d5

Browse files
committed
committer: add cli for committer benchmark
1 parent 51956c2 commit 9d988d5

File tree

4 files changed

+135
-4
lines changed

4 files changed

+135
-4
lines changed

crates/starknet_committer/src/block_committer/input.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ impl Default for ConfigImpl {
101101
}
102102
}
103103

104-
#[derive(Debug, Eq, PartialEq)]
104+
#[derive(Debug, Default, Eq, PartialEq)]
105105
pub struct Input<C: Config> {
106106
/// All relevant information for the state diff commitment.
107107
pub state_diff: StateDiff,

crates/starknet_committer/src/block_committer/state_diff_generator.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::block_committer::random_structs::RandomValue;
1111
#[path = "state_diff_generator_test.rs"]
1212
pub mod state_diff_generator_test;
1313

14-
pub(crate) const CONTRACT_ADDRESS: u32 = 500_u32;
14+
pub const CONTRACT_ADDRESS: u32 = 500_u32;
1515
pub(crate) const N_STORAGE_UPDATES: usize = 1000_usize;
1616

1717
pub fn generate_random_state_diff(rng: &mut StdRng) -> StateDiff {

crates/starknet_committer_and_os_cli/src/committer_cli/commands.rs

Lines changed: 127 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
1+
use std::time::Instant;
2+
3+
use rand::rngs::StdRng;
4+
use rand::SeedableRng;
5+
use starknet_api::core::ContractAddress;
16
use starknet_committer::block_committer::commit::commit_block;
2-
use starknet_committer::block_committer::input::Config;
7+
use starknet_committer::block_committer::input::{Config, ConfigImpl};
8+
use starknet_committer::block_committer::state_diff_generator::{
9+
generate_random_state_diff,
10+
CONTRACT_ADDRESS,
11+
};
12+
use starknet_patricia::hash::hash_trait::HashOutput;
13+
use starknet_patricia::patricia_merkle_tree::types::NodeIndex;
314
use starknet_patricia_storage::map_storage::MapStorage;
415
use tracing::info;
516
use tracing::level_filters::LevelFilter;
@@ -43,3 +54,118 @@ pub async fn commit(input: InputImpl, output_path: String, storage: MapStorage)
4354
output.contract_storage_root_hash, output.compiled_class_root_hash,
4455
);
4556
}
57+
58+
struct TimeMeasurement {
59+
timer: Option<Instant>,
60+
total_time: u128,
61+
results: Vec<f64>,
62+
}
63+
64+
impl TimeMeasurement {
65+
fn new() -> Self {
66+
Self { timer: None, total_time: 0, results: Vec::new() }
67+
}
68+
69+
fn start_measurement(&mut self) {
70+
self.timer = Some(Instant::now());
71+
}
72+
73+
fn stop_measurement(&mut self, norm: usize) {
74+
let duration = self.timer.expect("stop measurement before starting").elapsed();
75+
info!(
76+
"Time elapsed for iteration {}: {} milliseconds",
77+
self.n_results(),
78+
duration.as_millis()
79+
);
80+
self.total_time += duration.as_millis();
81+
self.results.push(duration.as_micros() as f64 / norm as f64);
82+
}
83+
84+
fn n_results(&self) -> usize {
85+
self.results.len()
86+
}
87+
88+
fn average_time(&self) -> f64 {
89+
self.total_time as f64 / self.n_results() as f64
90+
}
91+
92+
fn average_window_time(&self, window_size: usize) -> Vec<f64> {
93+
let mut averages = Vec::new();
94+
// Takes only the full windows, so if the last window is smaller than `window_size`, it is
95+
// ignored.
96+
let n_windows = self.n_results() / window_size;
97+
for i in 0..n_windows {
98+
let window_start = i * window_size;
99+
let sum: f64 = self.results[window_start..window_start + window_size].iter().sum();
100+
averages.push(sum / window_size as f64);
101+
}
102+
averages
103+
}
104+
105+
fn pretty_print(&self, window_size: usize) {
106+
if self.n_results() == 0 {
107+
info!("No measurements were taken.");
108+
return;
109+
}
110+
111+
info!("Total time: {} milliseconds for {} iterations", self.total_time, self.n_results());
112+
info!("Average time: {:.2} milliseconds", self.average_time());
113+
114+
info!("Average time per window of {window_size} iterations:");
115+
let means = self.average_window_time(window_size);
116+
let max = means.iter().cloned().fold(f64::MIN, f64::max);
117+
for (i, m) in means.iter().enumerate() {
118+
let norm = m / max;
119+
let width = (norm * 40.0).round() as usize; // up tp 40 characters wide
120+
let bar = "█".repeat(width.max(1));
121+
println!("win {i:>4}: {m:>8.4} micro-second / fact | {bar}");
122+
}
123+
}
124+
}
125+
126+
pub async fn run_storage_benchmark() {
127+
let n_iterations = 10000;
128+
let seed = 42_u64; // Constant seed for reproducibility
129+
130+
let mut rng = StdRng::seed_from_u64(seed);
131+
let mut time_measurement = TimeMeasurement::new();
132+
133+
let mut storage = MapStorage::new();
134+
let mut contracts_trie_root_hash = HashOutput::default();
135+
let mut classes_trie_root_hash = HashOutput::default();
136+
137+
let contract_num: u128 = (CONTRACT_ADDRESS as u128) + NodeIndex::FIRST_LEAF.0.low();
138+
let contract_leaf: ContractAddress = contract_num.into();
139+
140+
for i in 0..n_iterations {
141+
info!("Committer storage benchmark iteration {}/{}", i + 1, n_iterations);
142+
let input = InputImpl {
143+
state_diff: generate_random_state_diff(&mut rng),
144+
contracts_trie_root_hash,
145+
classes_trie_root_hash,
146+
config: ConfigImpl::default(),
147+
};
148+
149+
// commit block and write to storage
150+
time_measurement.start_measurement();
151+
let serialized_filled_forest = SerializedForest(
152+
commit_block(input, &mut storage).await.expect("Failed to commit the given block."),
153+
);
154+
serialized_filled_forest.0.write_to_storage(&mut storage);
155+
156+
let n_new_facts = serialized_filled_forest
157+
.0
158+
.storage_tries
159+
.get(&contract_leaf)
160+
.unwrap()
161+
.tree_map
162+
.len();
163+
time_measurement.stop_measurement(n_new_facts);
164+
165+
166+
contracts_trie_root_hash = serialized_filled_forest.0.get_contract_root_hash();
167+
classes_trie_root_hash = serialized_filled_forest.0.get_compiled_class_root_hash();
168+
}
169+
170+
time_measurement.pretty_print(50);
171+
}

crates/starknet_committer_and_os_cli/src/committer_cli/run_committer_cli.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use tracing::level_filters::LevelFilter;
44
use tracing_subscriber::reload::Handle;
55
use tracing_subscriber::Registry;
66

7-
use crate::committer_cli::commands::parse_and_commit;
7+
use crate::committer_cli::commands::{parse_and_commit, run_storage_benchmark};
88
use crate::committer_cli::tests::python_tests::CommitterPythonTestRunner;
99
use crate::shared_utils::types::{run_python_test, IoArgs, PythonTestArg};
1010

@@ -22,6 +22,8 @@ enum Command {
2222
io_args: IoArgs,
2323
},
2424
PythonTest(PythonTestArg),
25+
/// Run the committer on random data with owned storage.
26+
StorageBenchmark,
2527
}
2628

2729
pub async fn run_committer_cli(
@@ -37,5 +39,8 @@ pub async fn run_committer_cli(
3739
Command::PythonTest(python_test_arg) => {
3840
run_python_test::<CommitterPythonTestRunner>(python_test_arg).await;
3941
}
42+
Command::StorageBenchmark => {
43+
run_storage_benchmark().await;
44+
}
4045
}
4146
}

0 commit comments

Comments
 (0)