Skip to content

Commit ee5735b

Browse files
committed
Add per-phase heap profiling with bench_heap_profile.sh
1 parent 8415149 commit ee5735b

7 files changed

Lines changed: 350 additions & 6 deletions

File tree

bin/cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ tikv-jemalloc-ctl = { version = "0.6", features = ["stats"], optional = true }
1414

1515
[features]
1616
jemalloc-stats = ["dep:tikv-jemalloc-ctl"]
17-
instruments = ["prover/instruments"]
17+
instruments = ["prover/instruments", "stark/instruments"]

bin/cli/src/main.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,12 @@ fn cmd_prove(elf_path: PathBuf, output_path: PathBuf, blowup: Option<u8>, time:
262262
#[cfg(feature = "jemalloc-stats")]
263263
let tracker = heap_tracker::HeapTracker::start();
264264

265+
#[cfg(all(feature = "jemalloc-stats", feature = "instruments"))]
266+
stark::instruments::set_heap_reader(|| {
267+
tikv_jemalloc_ctl::epoch::advance().ok();
268+
tikv_jemalloc_ctl::stats::allocated::read().unwrap_or(0)
269+
});
270+
265271
let start = Instant::now();
266272
let proof = match blowup {
267273
Some(b) => {

crypto/stark/src/instruments.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,34 @@
11
use std::cell::RefCell;
2+
use std::sync::OnceLock;
23
use std::sync::atomic::{AtomicU64, Ordering};
34
use std::time::Duration;
45

6+
static HEAP_READER: OnceLock<fn() -> usize> = OnceLock::new();
7+
8+
pub fn set_heap_reader(f: fn() -> usize) {
9+
let _ = HEAP_READER.set(f);
10+
}
11+
12+
pub fn heap_bytes() -> Option<usize> {
13+
HEAP_READER.get().map(|f| f())
14+
}
15+
16+
fn heap_mb() -> Option<usize> {
17+
heap_bytes().map(|b| b / (1024 * 1024))
18+
}
19+
20+
pub type HeapSnapshot = (&'static str, usize);
21+
22+
pub fn snap(label: &'static str) -> Option<HeapSnapshot> {
23+
heap_mb().map(|mb| (label, mb))
24+
}
25+
26+
pub struct ProveHeapProfile {
27+
pub after_execute: Option<usize>,
28+
pub after_trace_build: Option<usize>,
29+
pub after_air: Option<usize>,
30+
}
31+
532
/// Sub-operation timing breakdown for a single table in Rounds 2-4.
633
#[derive(Clone, Debug, Default)]
734
pub struct TableSubOps {
@@ -49,6 +76,7 @@ pub struct MultiProveTiming {
4976
pub round1_sub: Round1SubOps,
5077
/// (name, rows, duration, sub_ops) per table for rounds 2-4.
5178
pub table_timings: Vec<(String, usize, Duration, TableSubOps)>,
79+
pub heap_snapshots: Vec<HeapSnapshot>,
5280
}
5381

5482
/// Round 1 sub-timings: atomics so parallel rayon workers can accumulate safely.

crypto/stark/src/prover.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,8 @@ pub trait IsStarkProver<
14961496

14971497
#[cfg(feature = "instruments")]
14981498
crate::instruments::reset_all();
1499+
#[cfg(feature = "instruments")]
1500+
let mut heap_snaps: Vec<crate::instruments::HeapSnapshot> = Vec::new();
14991501

15001502
let num_airs = air_trace_pairs.len();
15011503

@@ -1547,6 +1549,10 @@ pub trait IsStarkProver<
15471549

15481550
#[cfg(feature = "instruments")]
15491551
let prepass_elapsed = phase_start.elapsed();
1552+
#[cfg(feature = "instruments")]
1553+
if let Some(s) = crate::instruments::snap("after pool alloc") {
1554+
heap_snaps.push(s);
1555+
}
15501556

15511557
// =====================================================================
15521558
// Round 1, Phase A: Commit all main traces (parallel in chunks of K)
@@ -1609,6 +1615,10 @@ pub trait IsStarkProver<
16091615

16101616
#[cfg(feature = "instruments")]
16111617
let main_commits_elapsed = phase_start.elapsed();
1618+
#[cfg(feature = "instruments")]
1619+
if let Some(s) = crate::instruments::snap("after main commits") {
1620+
heap_snaps.push(s);
1621+
}
16121622

16131623
// =====================================================================
16141624
// Round 1, Phase B: Sample shared LogUp challenges
@@ -1655,6 +1665,10 @@ pub trait IsStarkProver<
16551665

16561666
#[cfg(feature = "instruments")]
16571667
let aux_build_elapsed = phase_start.elapsed();
1668+
#[cfg(feature = "instruments")]
1669+
if let Some(s) = crate::instruments::snap("after aux build") {
1670+
heap_snaps.push(s);
1671+
}
16581672

16591673
// Pass 2: Parallel fork transcript → extract → LDE → commit in chunks of K.
16601674
// Each table gets its own transcript fork and pool set.
@@ -1755,6 +1769,10 @@ pub trait IsStarkProver<
17551769

17561770
#[cfg(feature = "instruments")]
17571771
let aux_commit_elapsed = phase_start.elapsed();
1772+
#[cfg(feature = "instruments")]
1773+
if let Some(s) = crate::instruments::snap("after aux commit") {
1774+
heap_snaps.push(s);
1775+
}
17581776

17591777
#[cfg(feature = "debug-checks")]
17601778
{
@@ -1897,6 +1915,7 @@ pub trait IsStarkProver<
18971915
rounds_2_4: phase_start.elapsed(),
18981916
round1_sub: crate::instruments::take_r1_sub(),
18991917
table_timings,
1918+
heap_snapshots: heap_snaps,
19001919
});
19011920
}
19021921

prover/src/instruments.rs

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ pub fn print_report(
5757
trace_build: Duration,
5858
air_construction: Duration,
5959
total: Duration,
60+
heap_before: Option<usize>,
61+
heap_profile: &stark::instruments::ProveHeapProfile,
6062
) {
6163
let mp = stark::instruments::take();
6264

@@ -69,7 +71,7 @@ pub fn print_report(
6971
row_top("Trace build", trace_build, total);
7072
row_top("AIR construction", air_construction, total);
7173

72-
if let Some(mp) = mp {
74+
if let Some(ref mp) = mp {
7375
let round1 = mp.main_commits + mp.aux_build + mp.aux_commit;
7476

7577
row_top("Pre-pass (domains/twiddles)", mp.prepass, total);
@@ -177,10 +179,7 @@ pub fn print_report(
177179
("R4 queries & openings", total_queries),
178180
];
179181
sub_ops.sort_by(|a, b| b.1.cmp(&a.1));
180-
eprintln!(
181-
" {}",
182-
" \u{2500}\u{2500} sub-operation totals (all tables) \u{2500}\u{2500}",
183-
);
182+
eprintln!(" \u{2500}\u{2500} sub-operation totals (all tables) \u{2500}\u{2500}",);
184183
for (label, dur) in &sub_ops {
185184
row_sub(&format!(" {label}"), *dur, total);
186185
}
@@ -214,4 +213,34 @@ pub fn print_report(
214213
eprintln!(" {}", "─".repeat(58));
215214
eprintln!(" {:<36} {:>7.2}s", "TOTAL", total.as_secs_f64());
216215
eprintln!();
216+
217+
let mb = |b: usize| b / (1024 * 1024);
218+
if let Some(before) = heap_before {
219+
eprintln!("=== HEAP PROFILE (MB) ===");
220+
eprintln!(" {:<36} {:>8} {:>8}", "Phase", "Heap", "Delta");
221+
eprintln!(" {}", "─".repeat(56));
222+
let mut prev = before;
223+
let mut print_row = |label: &str, val: Option<usize>| {
224+
if let Some(v) = val {
225+
let cur = mb(v);
226+
let delta = mb(v) as isize - mb(prev) as isize;
227+
eprintln!(" {:<36} {:>7} {:>+8}", label, cur, delta);
228+
prev = v;
229+
}
230+
};
231+
print_row("After execute", heap_profile.after_execute);
232+
print_row("After trace build", heap_profile.after_trace_build);
233+
print_row("After AIR construction", heap_profile.after_air);
234+
if let Some(ref mp_data) = mp {
235+
for (label, snap_mb) in &mp_data.heap_snapshots {
236+
let snap_bytes = snap_mb * (1024 * 1024);
237+
let cur = *snap_mb;
238+
let delta = cur as isize - mb(prev) as isize;
239+
eprintln!(" {:<36} {:>7} {:>+8}", label, cur, delta);
240+
prev = snap_bytes;
241+
}
242+
}
243+
eprintln!(" {}", "─".repeat(56));
244+
eprintln!();
245+
}
217246
}

prover/src/lib.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,8 @@ pub fn prove_with_options(
476476
) -> Result<VmProof, Error> {
477477
#[cfg(feature = "instruments")]
478478
let total_start = std::time::Instant::now();
479+
#[cfg(feature = "instruments")]
480+
let heap_before = stark::instruments::heap_bytes();
479481

480482
// Phase 1: Execute (ELF load + run)
481483
#[cfg(feature = "instruments")]
@@ -489,6 +491,8 @@ pub fn prove_with_options(
489491

490492
#[cfg(feature = "instruments")]
491493
let execute_elapsed = phase_start.elapsed();
494+
#[cfg(feature = "instruments")]
495+
let heap_after_execute = stark::instruments::heap_bytes();
492496

493497
// Phase 2: Trace build
494498
#[cfg(feature = "instruments")]
@@ -500,6 +504,8 @@ pub fn prove_with_options(
500504

501505
#[cfg(feature = "instruments")]
502506
let trace_build_elapsed = phase_start.elapsed();
507+
#[cfg(feature = "instruments")]
508+
let heap_after_trace = stark::instruments::heap_bytes();
503509

504510
// Phase 3: AIR construction
505511
#[cfg(feature = "instruments")]
@@ -516,6 +522,8 @@ pub fn prove_with_options(
516522

517523
#[cfg(feature = "instruments")]
518524
let air_elapsed = phase_start.elapsed();
525+
#[cfg(feature = "instruments")]
526+
let heap_after_air = stark::instruments::heap_bytes();
519527

520528
let runtime_page_ranges = traces.runtime_page_ranges();
521529

@@ -533,6 +541,12 @@ pub fn prove_with_options(
533541
trace_build_elapsed,
534542
air_elapsed,
535543
total_start.elapsed(),
544+
heap_before,
545+
&stark::instruments::ProveHeapProfile {
546+
after_execute: heap_after_execute,
547+
after_trace_build: heap_after_trace,
548+
after_air: heap_after_air,
549+
},
536550
);
537551
}
538552

0 commit comments

Comments
 (0)