Skip to content

Parallelize the two dominant bitwise-histogram sources internally#794

Merged
ColoCarletti merged 1 commit into
perf/tracegen-cpu-optimizationsfrom
review/pr786-parallel-histogram
Jul 8, 2026
Merged

Parallelize the two dominant bitwise-histogram sources internally#794
ColoCarletti merged 1 commit into
perf/tracegen-cpu-optimizationsfrom
review/pr786-parallel-histogram

Conversation

@MauroToscano

Copy link
Copy Markdown
Contributor

Follow-up to #786's review (efficiency finding #6). Perf-only change — meant to be ABBA'd against perf/tracegen-cpu-optimizations before merging.

Problem

The bitwise multiplicity phase (p4_bitwise_collect) ran each source as one atomic collector over a capped par_chunks. The two dominant sources — the in-walk lookups and MEMW_R, each tens of millions of items — therefore each pinned a single core while the other cores finished their trivial chunks and idled; par_chunks is static/contiguous so rayon can't work-steal into the busy chunk. The in-walk fold also ran serially before the parallel region opened.

Change

  • Pull the two heavy sources out of the atomic collector list and split each into ~cap row-range slices (cap = min(threads, 8)).
  • Round-robin every unit (whole collectors + the heavy slices) into exactly cap buckets, one 80 MiB histogram each, then par_iter().map(fold).reduce_with(merge).
  • The in-walk fold now happens inside the parallel region instead of serially before it.

Peak memory is unchanged (still ≤cap concurrent histograms — same as the previous par_chunks(≤8) cap); the heavy work is just spread across buckets instead of piled onto one core. add_ops/bump/merge form a commutative monoid, so multiplicities are byte-identical regardless of partition.

PAGE is left whole for now; if the ABBA shows it's the new bottleneck it can be sliced the same way in a follow-up.

Correctness

  • Full cargo test -p lambda-vm-prover --lib: 502 passed (only the 5 known missing rust-guest ELF fixtures fail, at file-read). The prove+verify tests passing is the real check — a wrong multiplicity would unbalance the BITWISE bus and fail verification.
  • make lint, cargo clippy --all-targets, cargo fmt --check all clean; both parallel and --no-default-features (serial) paths compile.

Expected

Should shrink p4_bitwise_collect wall time (the MEMW_R slice was ~1–2 s single-core at 10-tx); most visible on GPU where trace-gen is a larger share of prove time. Please ABBA on the CPU and GPU paths before merging — if it doesn't move the needle, it's fine to close.

The bitwise multiplicity phase ran each source as one atomic collector across
a capped par_chunks, so the two dominant sources — the in-walk lookups and
MEMW_R (each tens of millions of items) — each pinned a single core while the
rest idled, and the in-walk fold ran serially before the parallel region.

Split those two sources into ~cap row-range slices and round-robin every unit
(whole collectors + the heavy slices) into exactly cap buckets, one 80 MiB
histogram each. Peak memory is unchanged (still cap concurrent histograms), but
the heavy work is now spread across buckets instead of piled onto one core, and
the in-walk fold moved into the parallel region. add_ops/bump/merge form a
commutative monoid, so multiplicities are byte-identical (prove+verify pass).

PAGE is left whole for now; if it becomes the bottleneck it can be sliced the
same way in a follow-up.
@MauroToscano

Copy link
Copy Markdown
Contributor Author

Measured on a 16C/32T CPU box (125 GiB RAM), ethrex 5-transfer

Phase-level (--features instruments, p4_bitwise_collect, 3 runs each, cap=8):

baseline (6cb96d8) this PR (610245f)
runs 910 / 881 / 904 ms 834 / 792 / 796 ms
mean 898 ms 807 ms

~10% off the phase (~91 ms), distributions barely overlap, no regression.

End-to-end (ABBA, 32 pairs, same box): median −0.27%, mean −0.17%, 95% CI [−0.54%, +0.19%], Wilcoxon p=0.26 — inconclusive end-to-end because the phase is only 2.7% of prove time on CPU. The 91 ms saving / 33.2 s prove = 0.27%, which matches the ABBA median exactly — the two instruments agree, the effect is just small on CPU.

Why this still matters: on GPU the FFT/Merkle/LDE move to the device but trace-gen stays CPU work, so the bitwise phase is a much larger share of end-to-end there — the same ~10% phase cut should land around ~1% end-to-end. That's the configuration worth measuring before merging.

Cap note: the phase is memory-bandwidth-bound (random writes into 80 MiB histograms) — splitting across 8 buckets recovered ~10%, not ~8×, so raising the clamp(1,8) cap would add merge/zero traffic for little gain. Memory headroom is a non-issue (125 GiB), but bandwidth is the wall, so cap=8 is left as-is.

Correctness unchanged: full cargo test -p lambda-vm-prover --lib passes (prove+verify → byte-identical multiplicities).

@ColoCarletti ColoCarletti merged commit baa9e73 into perf/tracegen-cpu-optimizations Jul 8, 2026
14 checks passed
@ColoCarletti ColoCarletti deleted the review/pr786-parallel-histogram branch July 8, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants