Skip to content

Commit 7e32bf7

Browse files
committed
feat(stm): log IVC benchmark progress to stderr during long runs
A no-filter run does minutes of silent keygen and proving; emit per-phase progress to stderr (environment build, per-path prove/verify/fold, setup cold/warm) before each step so runs visibly advance. Logging happens outside the timed regions, so measurements are unaffected.
1 parent 1699448 commit 7e32bf7

1 file changed

Lines changed: 53 additions & 27 deletions

File tree

mithril-stm/benches/ivc_halo2_snark.rs

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,14 @@ struct PathTimings {
126126
fold: Option<Duration>,
127127
}
128128

129-
/// Times one operation once (a single observation) when `run` is true, else returns `None`. The result
130-
/// is `black_box`-ed so the work is not optimised away.
131-
fn timed<T>(run: bool, operation: impl FnOnce() -> T) -> Option<Duration> {
129+
/// Times one operation once (a single observation) when `run` is true, else returns `None`. Prints a
130+
/// progress line to stderr **before** starting the clock (so logging is never counted in the timing) and
131+
/// `black_box`-es the result so the work is not optimised away.
132+
fn timed<T>(label: &str, run: bool, operation: impl FnOnce() -> T) -> Option<Duration> {
132133
if !run {
133134
return None;
134135
}
136+
eprintln!(" {label} …");
135137
let start = Instant::now();
136138
black_box(operation());
137139
Some(start.elapsed())
@@ -147,31 +149,48 @@ fn measure_path(
147149
env: &IvcBenchEnv,
148150
step: &PreparedStep,
149151
) -> PathTimings {
152+
// Each id doubles as the stderr progress label, so the log line matches the `--list` ids exactly.
153+
let prove_poseidon = {
154+
let id = format!("ivc/{name}/prove/poseidon");
155+
timed(&id, selected(filter, &id), || {
156+
env.prove_poseidon(step).expect("poseidon prove should succeed")
157+
})
158+
};
159+
let prove_blake2b = {
160+
let id = format!("ivc/{name}/prove/blake2b");
161+
timed(&id, selected(filter, &id), || {
162+
env.prove_blake2b(step).expect("blake2b prove should succeed")
163+
})
164+
};
165+
let verify_full = {
166+
let id = format!("ivc/{name}/verify/full");
167+
timed(&id, selected(filter, &id), || {
168+
env.verify_full(step).expect("full verification should succeed")
169+
})
170+
};
171+
let verify_kzg_opening = {
172+
let id = format!("ivc/{name}/verify/kzg_opening");
173+
timed(&id, selected(filter, &id), || {
174+
env.verify_kzg_opening(step).expect("kzg opening should succeed")
175+
})
176+
};
177+
let fold = if is_genesis {
178+
None
179+
} else {
180+
let id = format!("ivc/{name}/fold");
181+
timed(&id, selected(filter, &id), || {
182+
env.fold_accumulators(step).expect("non-genesis fold present")
183+
})
184+
};
185+
150186
PathTimings {
151187
name,
152188
proof_size: step.proof_size(),
153-
prove_poseidon: timed(
154-
selected(filter, &format!("ivc/{name}/prove/poseidon")),
155-
|| env.prove_poseidon(step).expect("poseidon prove should succeed"),
156-
),
157-
prove_blake2b: timed(
158-
selected(filter, &format!("ivc/{name}/prove/blake2b")),
159-
|| env.prove_blake2b(step).expect("blake2b prove should succeed"),
160-
),
161-
verify_full: timed(selected(filter, &format!("ivc/{name}/verify/full")), || {
162-
env.verify_full(step).expect("full verification should succeed")
163-
}),
164-
verify_kzg_opening: timed(
165-
selected(filter, &format!("ivc/{name}/verify/kzg_opening")),
166-
|| env.verify_kzg_opening(step).expect("kzg opening should succeed"),
167-
),
168-
fold: if is_genesis {
169-
None
170-
} else {
171-
timed(selected(filter, &format!("ivc/{name}/fold")), || {
172-
env.fold_accumulators(step).expect("non-genesis fold present")
173-
})
174-
},
189+
prove_poseidon,
190+
prove_blake2b,
191+
verify_full,
192+
verify_kzg_opening,
193+
fold,
175194
}
176195
}
177196

@@ -234,14 +253,20 @@ fn run_path_benches(filter: Option<&str>) {
234253
// One shared environment (a full recursive keygen) for the whole run; `cache` stays in scope (and
235254
// thus on disk) for the entire block. The keygen is timed once as the one-off `setup` cost.
236255
let cache = TempDir::new().expect("bench cache tempdir");
256+
eprintln!("building shared environment (recursive keygen); this dominates the runtime …");
237257
let setup_start = Instant::now();
238258
let env = IvcBenchEnv::new(cache.path()).expect("IVC bench environment should build");
239259
let setup = setup_start.elapsed();
260+
eprintln!(
261+
"environment ready in {:.1}s; measuring paths …",
262+
setup.as_secs_f64()
263+
);
240264

241265
let rows: Vec<PathTimings> = PATHS
242266
.iter()
243267
.filter(|(path, name)| path_selected(filter, *path, name))
244268
.map(|(path, name)| {
269+
eprintln!("[{name}] preparing fixture (untimed) …");
245270
let step = env.prepare_step(*path).expect("fixture preparation should succeed");
246271
measure_path(
247272
filter,
@@ -268,6 +293,7 @@ fn observe<T>(operation: impl FnOnce() -> T) -> Duration {
268293
/// per-path environment.
269294
fn run_setup_benches(filter: Option<&str>) {
270295
let srs = selected(filter, "ivc/setup/srs").then(|| {
296+
eprintln!("measuring setup: ivc/setup/srs (cold then warm) …");
271297
let dir = TempDir::new().expect("bench cache tempdir");
272298
let cold = observe(|| {
273299
IvcBenchEnv::measure_srs_cold_start(dir.path()).expect("srs cold-start should succeed")
@@ -279,8 +305,8 @@ fn run_setup_benches(filter: Option<&str>) {
279305
});
280306
let keys = selected(filter, "ivc/setup/keys").then(|| {
281307
eprintln!(
282-
"note: 'ivc/setup/keys' runs a full recursive keygen for the cold measurement \
283-
(minutes, GB-scale RAM)."
308+
"measuring setup: ivc/setup/keys (cold then warm) — the cold run does a full recursive \
309+
keygen (minutes, GB-scale RAM)"
284310
);
285311
let dir = TempDir::new().expect("bench cache tempdir");
286312
let srs = IvcBenchEnv::measure_srs_cold_start(dir.path())

0 commit comments

Comments
 (0)