Skip to content

Commit 3f68836

Browse files
committed
starknet_committer,starknet_committer_cli: move TimeMesaurment to the committer crate
To allow inner calls benchmarking
1 parent 3a6490f commit 3f68836

File tree

6 files changed

+151
-145
lines changed

6 files changed

+151
-145
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/starknet_committer/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@ description = "Computes and manages Starknet state."
88

99
[features]
1010
testing = ["starknet_patricia/testing"]
11+
benchmark = ["csv"]
1112

1213
[dependencies]
14+
csv = { workspace = true, optional = true }
1315
ethnum.workspace = true
1416
hex.workspace = true
1517
pretty_assertions.workspace = true

crates/starknet_committer/src/block_committer.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ pub mod input;
55
pub mod random_structs;
66
#[cfg(any(feature = "testing", test))]
77
pub mod state_diff_generator;
8+
#[cfg(feature = "benchmark")]
9+
pub mod timing_util;
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
use std::fs::{self, File};
2+
use std::time::Instant;
3+
4+
use csv::Writer;
5+
use tracing::info;
6+
pub struct TimeMeasurement {
7+
timer: Option<Instant>,
8+
total_time: u128, // Total duration of all blocks (milliseconds).
9+
per_fact_durations: Vec<u64>, // Average duration (microseconds) per new fact in a block.
10+
n_facts: Vec<usize>,
11+
block_durations: Vec<u64>, // Duration of a block (milliseconds).
12+
facts_in_db: Vec<usize>, // Number of facts in the DB prior to the current block.
13+
total_facts: usize,
14+
}
15+
16+
impl TimeMeasurement {
17+
pub fn new(n_iterations: usize) -> Self {
18+
Self {
19+
timer: None,
20+
total_time: 0,
21+
per_fact_durations: Vec::with_capacity(n_iterations),
22+
n_facts: Vec::with_capacity(n_iterations),
23+
block_durations: Vec::with_capacity(n_iterations),
24+
facts_in_db: Vec::with_capacity(n_iterations),
25+
total_facts: 0,
26+
}
27+
}
28+
29+
pub fn start_measurement(&mut self) {
30+
self.timer = Some(Instant::now());
31+
}
32+
33+
pub fn stop_measurement(&mut self, facts_count: usize) {
34+
let duration =
35+
self.timer.expect("stop_measurement called before start_measurement").elapsed();
36+
info!(
37+
"Time elapsed for iteration {}: {} milliseconds",
38+
self.n_results(),
39+
duration.as_millis()
40+
);
41+
let millis = duration.as_millis();
42+
self.total_time += millis;
43+
#[allow(clippy::as_conversions)]
44+
self.per_fact_durations
45+
.push(duration.div_f32(facts_count as f32).as_micros().try_into().unwrap());
46+
self.block_durations.push(millis.try_into().unwrap());
47+
self.n_facts.push(facts_count);
48+
self.facts_in_db.push(self.total_facts);
49+
self.total_facts += facts_count;
50+
}
51+
52+
pub fn n_results(&self) -> usize {
53+
self.block_durations.len()
54+
}
55+
56+
/// Returns the average time per block (milliseconds).
57+
fn block_average_time(&self) -> f64 {
58+
#[allow(clippy::as_conversions)]
59+
{
60+
self.total_time as f64 / self.n_results() as f64
61+
}
62+
}
63+
64+
/// Returns the average time per fact over a window of `window_size` blocks (microseconds).
65+
fn average_window_time(&self, window_size: usize) -> Vec<f64> {
66+
let mut averages = Vec::new(); // In milliseconds.
67+
// Takes only the full windows, so if the last window is smaller than `window_size`, it is
68+
// ignored.
69+
let n_windows = self.n_results() / window_size;
70+
for i in 0..n_windows {
71+
let window_start = i * window_size;
72+
let sum: u64 =
73+
self.block_durations[window_start..window_start + window_size].iter().sum();
74+
let sum_of_facts: usize =
75+
self.n_facts[window_start..window_start + window_size].iter().sum();
76+
#[allow(clippy::as_conversions)]
77+
averages.push(1000.0 * sum as f64 / sum_of_facts as f64);
78+
}
79+
averages
80+
}
81+
82+
pub fn pretty_print(&self, window_size: usize) {
83+
if self.n_results() == 0 {
84+
println!("No measurements were taken.");
85+
return;
86+
}
87+
88+
println!(
89+
"Total time: {} milliseconds for {} iterations.",
90+
self.total_time,
91+
self.n_results()
92+
);
93+
println!(
94+
"Average block time: {:.2} milliseconds.
95+
",
96+
self.block_average_time()
97+
);
98+
99+
println!("Average time per window of {window_size} iterations:");
100+
let means = self.average_window_time(window_size);
101+
let max = means.iter().cloned().fold(f64::MIN, f64::max);
102+
// Print a graph visualization of block times.
103+
for (i, fact_duration) in means.iter().enumerate() {
104+
let norm = fact_duration / max;
105+
#[allow(clippy::as_conversions)]
106+
let width = (norm * 40.0).round() as usize; // up tp 40 characters wide
107+
let bar = "█".repeat(width.max(1));
108+
println!("win {i:>4}: {fact_duration:>8.4} microsecond / fact | {bar}");
109+
}
110+
}
111+
112+
pub fn to_csv(&self, path: &str, output_dir: &str) {
113+
fs::create_dir_all(output_dir).expect("Failed to create output directory.");
114+
let file =
115+
File::create(format!("{output_dir}/{path}")).expect("Failed to create CSV file.");
116+
let mut wtr = Writer::from_writer(file);
117+
wtr.write_record([
118+
"block_number",
119+
"n_facts",
120+
"facts_in_db",
121+
"time_per_fact_micros",
122+
"block_duration_millis",
123+
])
124+
.expect("Failed to write CSV header.");
125+
for (i, (((&per_fact, &n_facts), &duration), &facts_in_db)) in self
126+
.per_fact_durations
127+
.iter()
128+
.zip(self.n_facts.iter())
129+
.zip(self.block_durations.iter())
130+
.zip(self.facts_in_db.iter())
131+
.enumerate()
132+
{
133+
wtr.write_record(&[
134+
i.to_string(),
135+
n_facts.to_string(),
136+
facts_in_db.to_string(),
137+
per_fact.to_string(),
138+
duration.to_string(),
139+
])
140+
.expect("Failed to write CSV record.");
141+
}
142+
wtr.flush().expect("Failed to flush CSV writer.");
143+
}
144+
}

crates/starknet_committer_cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ workspace = true
1313
clap = { workspace = true, features = ["cargo", "derive"] }
1414
csv.workspace = true
1515
rand.workspace = true
16-
starknet_committer = { workspace = true, features = ["testing"] }
16+
starknet_committer = { workspace = true, features = ["benchmark", "testing"] }
1717
# TODO(Tzahi): Remove once tracing is moved to a common location.
1818
starknet_committer_and_os_cli.workspace = true
1919
starknet_patricia.workspace = true

crates/starknet_committer_cli/src/commands.rs

Lines changed: 1 addition & 144 deletions
Original file line numberDiff line numberDiff line change
@@ -1,158 +1,15 @@
1-
use std::fs::{self, File};
2-
use std::time::Instant;
3-
4-
use csv::Writer;
51
use rand::rngs::SmallRng;
62
use rand::SeedableRng;
73
use starknet_committer::block_committer::commit::commit_block;
84
use starknet_committer::block_committer::input::{ConfigImpl, Input};
95
use starknet_committer::block_committer::state_diff_generator::generate_random_state_diff;
6+
use starknet_committer::block_committer::timing_util::TimeMeasurement;
107
use starknet_patricia::hash::hash_trait::HashOutput;
118
use starknet_patricia_storage::map_storage::MapStorage;
129
use tracing::info;
1310

1411
pub type InputImpl = Input<ConfigImpl>;
1512

16-
struct TimeMeasurement {
17-
timer: Option<Instant>,
18-
total_time: u128, // Total duration of all blocks (milliseconds).
19-
per_fact_durations: Vec<u64>, // Average duration (microseconds) per new fact in a block.
20-
n_facts: Vec<usize>,
21-
block_durations: Vec<u64>, // Duration of a block (milliseconds).
22-
facts_in_db: Vec<usize>, // Number of facts in the DB prior to the current block.
23-
total_facts: usize,
24-
}
25-
26-
impl TimeMeasurement {
27-
fn new(n_iterations: usize) -> Self {
28-
Self {
29-
timer: None,
30-
total_time: 0,
31-
per_fact_durations: Vec::with_capacity(n_iterations),
32-
n_facts: Vec::with_capacity(n_iterations),
33-
block_durations: Vec::with_capacity(n_iterations),
34-
facts_in_db: Vec::with_capacity(n_iterations),
35-
total_facts: 0,
36-
}
37-
}
38-
39-
fn start_measurement(&mut self) {
40-
self.timer = Some(Instant::now());
41-
}
42-
43-
fn stop_measurement(&mut self, facts_count: usize) {
44-
let duration =
45-
self.timer.expect("stop_measurement called before start_measurement").elapsed();
46-
info!(
47-
"Time elapsed for iteration {}: {} milliseconds",
48-
self.n_results(),
49-
duration.as_millis()
50-
);
51-
let millis = duration.as_millis();
52-
self.total_time += millis;
53-
#[allow(clippy::as_conversions)]
54-
self.per_fact_durations
55-
.push(duration.div_f32(facts_count as f32).as_micros().try_into().unwrap());
56-
self.block_durations.push(millis.try_into().unwrap());
57-
self.n_facts.push(facts_count);
58-
self.facts_in_db.push(self.total_facts);
59-
self.total_facts += facts_count;
60-
}
61-
62-
fn n_results(&self) -> usize {
63-
self.block_durations.len()
64-
}
65-
66-
/// Returns the average time per block (milliseconds).
67-
fn block_average_time(&self) -> f64 {
68-
#[allow(clippy::as_conversions)]
69-
{
70-
self.total_time as f64 / self.n_results() as f64
71-
}
72-
}
73-
74-
/// Returns the average time per fact over a window of `window_size` blocks (microseconds).
75-
fn average_window_time(&self, window_size: usize) -> Vec<f64> {
76-
let mut averages = Vec::new(); // In milliseconds.
77-
// Takes only the full windows, so if the last window is smaller than `window_size`, it is
78-
// ignored.
79-
let n_windows = self.n_results() / window_size;
80-
for i in 0..n_windows {
81-
let window_start = i * window_size;
82-
let sum: u64 =
83-
self.block_durations[window_start..window_start + window_size].iter().sum();
84-
let sum_of_facts: usize =
85-
self.n_facts[window_start..window_start + window_size].iter().sum();
86-
#[allow(clippy::as_conversions)]
87-
averages.push(1000.0 * sum as f64 / sum_of_facts as f64);
88-
}
89-
averages
90-
}
91-
92-
fn pretty_print(&self, window_size: usize) {
93-
if self.n_results() == 0 {
94-
println!("No measurements were taken.");
95-
return;
96-
}
97-
98-
println!(
99-
"Total time: {} milliseconds for {} iterations.",
100-
self.total_time,
101-
self.n_results()
102-
);
103-
println!(
104-
"Average block time: {:.2} milliseconds.
105-
",
106-
self.block_average_time()
107-
);
108-
109-
println!("Average time per window of {window_size} iterations:");
110-
let means = self.average_window_time(window_size);
111-
let max = means.iter().cloned().fold(f64::MIN, f64::max);
112-
// Print a graph visualization of block times.
113-
for (i, fact_duration) in means.iter().enumerate() {
114-
let norm = fact_duration / max;
115-
#[allow(clippy::as_conversions)]
116-
let width = (norm * 40.0).round() as usize; // up tp 40 characters wide
117-
let bar = "█".repeat(width.max(1));
118-
println!("win {i:>4}: {fact_duration:>8.4} microsecond / fact | {bar}");
119-
}
120-
}
121-
122-
fn to_csv(&self, path: &str, output_dir: &str) {
123-
fs::create_dir_all(output_dir).expect("Failed to create output directory.");
124-
let file =
125-
File::create(format!("{output_dir}/{path}")).expect("Failed to create CSV file.");
126-
let mut wtr = Writer::from_writer(file);
127-
wtr.write_record([
128-
"block_number",
129-
"n_facts",
130-
"facts_in_db",
131-
"time_per_fact_micros",
132-
"block_duration_millis",
133-
])
134-
.expect("Failed to write CSV header.");
135-
for (i, (((&per_fact, &n_facts), &duration), &facts_in_db)) in self
136-
.per_fact_durations
137-
.iter()
138-
.zip(self.n_facts.iter())
139-
.zip(self.block_durations.iter())
140-
.zip(self.facts_in_db.iter())
141-
.enumerate()
142-
{
143-
wtr.write_record(&[
144-
i.to_string(),
145-
n_facts.to_string(),
146-
facts_in_db.to_string(),
147-
per_fact.to_string(),
148-
duration.to_string(),
149-
])
150-
.expect("Failed to write CSV record.");
151-
}
152-
wtr.flush().expect("Failed to flush CSV writer.");
153-
}
154-
}
155-
15613
/// Runs the committer on n_iterations random generated blocks.
15714
/// Prints the time measurement to the console and saves statistics to a CSV file in the given
15815
/// output directory.

0 commit comments

Comments
 (0)