Uniform table cap + Twiddle dedup + FRI fold parallelism#497
Uniform table cap + Twiddle dedup + FRI fold parallelism#497gabrielbosio wants to merge 5 commits into
Conversation
be10f79 to
40aad85
Compare
Tables with effective width < 42 were sized at 2^21, producing single large chunks. Capping at 2^20 produces 2x more chunks of the standard size, improving parallel throughput and keeping peak memory per chunk uniform across all tables.
Tables sharing the same lde_size now reuse a single Arc<LdeTwiddles> instead of each allocating their own copy. This avoids redundant twiddle computation and allocation when multiple tables happen to have the same trace length (e.g., all 2^19-row tables after the cap change).
Under the `parallel` feature, fold each conjugate pair concurrently using `par_chunks(2).zip(inv_twiddles.par_iter())`, collecting into a new Vec of half length. The sequential path (no `parallel` feature) is unchanged. This avoids the index aliasing issue from an in-place interleaved write.
Replace the per-row `Vec` allocation inside the iterator with a thread-local buffer via Rayon's `map_init` (parallel path) or a single buffer allocated outside the loop (sequential path). This avoids one heap allocation per row during Merkle tree construction.
40aad85 to
cf1c8d9
Compare
|
/bench |
Benchmark — fib_iterative_8M (median of 3)Table parallelism: 32 (auto = cores / 3)
Commit: eeb860c · Baseline: built from main · Runner: self-hosted bench |
cf1c8d9 to
4fec818
Compare
|
/bench |
|
Capping 2^20 is not showing a difference against capping 2^19. |
4fec818 to
cf1c8d9
Compare
| &sum + &(&inv_twiddles[j] * &(zeta * &diff)) | ||
| }) | ||
| .collect(); | ||
| *evals = folded; |
There was a problem hiding this comment.
Low – parallel path always allocates a fresh Vec per fold level
*evals = folded drops the old allocation and replaces it. FRI folding runs ~log₂(N) times on the hot path, so this adds ~log₂(N) allocations compared to the sequential path.
One option is to reuse the buffer:
| *evals = folded; | |
| evals.clear(); | |
| evals.extend(folded); |
Or pass in a mutable scratch buffer so the caller can pre-allocate once and reuse across levels. The current approach is correct but leaves a performance opportunity on the table given this is the inner loop of FRI.
|
/bench |
2386f19 to
eeb860c
Compare
|
/bench |
|
#499 showed the same improvement and it's smaller. |
No description provided.