Skip to content

perf: fan out ComputeEvaluationsAtZeta across columns#8

Merged
ThomasPiellard merged 1 commit into
mainfrom
perf/parallel-evals-at-zeta
May 28, 2026
Merged

perf: fan out ComputeEvaluationsAtZeta across columns#8
ThomasPiellard merged 1 commit into
mainfrom
perf/parallel-evals-at-zeta

Conversation

@gbotrel

@gbotrel gbotrel commented May 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Each per-column evaluation at ζ is an independent FFTInverse + Horner pass. The previous parallel.Execute only fanned out over modules — single-module programs (the common shape, including the synth wide benchmark) saw every column evaluated serially in one goroutine. After step 1 (#7), evaluations-at-zeta is what the bench report flagged as the next-biggest serial chunk (~57% of wide-trace prove wall).

  • Gather every (poly, eval point) task across all modules into one flat list; parallel.Execute over it.
  • Inner FFTs run with fft.WithNbTasks(1) — the column-level fan-out already saturates the cores. The FFT option is forwarded through new variadic fft.Option args on poly.EvaluateAtExt / poly.ExtEvaluateAtExt.
  • Drive-by: replace the O(shift) manual mul loop for the rotation twiddle with Element.Exp (binary exponentiation).
  • Add prover/BenchmarkProveWide so the improvement stays visible in go test -bench.

Numbers

Wide-trace benchmark (synth, log2_rows=12, repetitions=256, 32 cores):

wall
baseline (main) 232.8 ms
this PR 178.0 ms
speedup 1.31×

Test plan

  • go test ./... -count=1 — all green
  • go vet ./... — only pre-existing copylocks warnings
  • go test -bench=BenchmarkProveWide -benchtime=5x ./prover/ — 1.31× speedup
  • BenchmarkProver in integration_test/gnark_plonk — unchanged (the existing multi-module path was already parallel)

🤖 Generated with Claude Code


Note

Medium Risk
Changes hot-path prove logic and parallel FFT behavior; correctness still depends on identical evaluation math, but oversubscription and ordering bugs would surface as proof failures rather than compile errors.

Overview
ComputeEvaluationsAtZeta no longer parallelizes only by module. It builds one flat list of per-column (and AIR chunk) evaluation tasks across all modules, runs parallel.Execute over that list, and writes results back into the proof. Each task still does FFTInverse + Horner via poly.EvaluateAtExt / poly.ExtEvaluateAtExt, but inner FFTs are forced to a single worker with fft.WithNbTasks(1) so nested parallelism does not oversubscribe CPUs.

Those poly helpers now accept optional fft.Option variadic args and forward them into FFTInverse / FFTInverseExt. Rotated-column evaluation points use omegaPow.Exp(module.D.Generator, …) instead of repeated multiplies.

A new BenchmarkProveWide in prover targets wide single-module traces where evaluations-at-ζ dominate prove time, so the column fan-out stays measurable in go test -bench.

Reviewed by Cursor Bugbot for commit 3dfefbf. Bugbot is set up for automated code reviews on this repo. Configure here.

Each per-column evaluation at ζ is an independent FFTInverse + Horner pass.
The previous parallel.Execute only fanned out over modules — single-module
programs (the common shape, including the synth wide benchmark) saw every
column evaluated serially in one goroutine.

Gather every (poly, evaluation point) pair across all modules into a single
task list and parallel.Execute over it. Inner FFTs run with WithNbTasks(1)
since the column-level fan-out already saturates the CPUs; the FFT option
is forwarded through new variadic fft.Option args on poly.EvaluateAtExt
and poly.ExtEvaluateAtExt.

Drive-by: replace the O(shift) manual mul loop for the rotation twiddle
with Element.Exp (binary exponentiation).

Wide-trace benchmark (synth, log2_rows=12, repetitions=256, 32 cores):

    BenchmarkProveWide  232.8 ms → 178.0 ms   (1.31×)

prover/bench_evals_test.go is added so the wide-shape improvement remains
visible in `go test -bench`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 27, 2026 20:28
@gbotrel gbotrel requested a review from ThomasPiellard May 27, 2026 20:32

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves prover throughput by parallelizing ComputeEvaluationsAtZeta across individual column evaluations (instead of only across modules), adds a way to cap inner FFT parallelism via forwarded fft.Options, and introduces a wide-trace benchmark to keep the speedup visible in go test -bench.

Changes:

  • Refactor ComputeEvaluationsAtZeta to build a flat per-column task list and parallel.Execute over it, while capping inner FFTs to fft.WithNbTasks(1).
  • Extend poly.EvaluateAtExt / poly.ExtEvaluateAtExt with variadic fft.Option forwarding for inner-FFT tuning.
  • Add BenchmarkProveWide to exercise the “single-module, many columns” shape where evaluations-at-zeta dominates.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
prover/prover.go Flattens and parallelizes evaluation-at-ζ work across columns; caps inner FFT parallelism.
internal/poly/ext.go Adds variadic FFT options to evaluation helpers so callers can control FFT tasking.
prover/bench_evals_test.go Adds a wide-trace benchmark to measure/track the evals-at-ζ speedup.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread prover/prover.go
Comment on lines +629 to +634
taskCap := 0
for i, moduleName := range moduleNames {
leaves := pr.program.Modules[moduleName].VanishingRelation.LeavesFull(config)
moduleLeaves[i] = leaves
taskCap += len(leaves)
}
Comment thread prover/prover.go
Comment on lines +643 to +647
if leaf.Type == expr.RotatedColumn {
shift := ((leaf.Shift % module.N) + module.N) % module.N
var omegaPow koalabear.Element
omegaPow.Exp(module.D.Generator, big.NewInt(int64(shift)))
evalPoint.MulByElement(&evalPoint, &omegaPow)
@ThomasPiellard

ThomasPiellard commented May 28, 2026

Copy link
Copy Markdown
Collaborator

looks good, but I want to test something else first -> compute all the Lagrange at zeta for the biggest module size once, and then use scalar products of a subset of those Lagrange at zeta (for smaller modules) to do batch evaluation directly

@ThomasPiellard ThomasPiellard merged commit 1f4d5f1 into main May 28, 2026
2 checks passed
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.

3 participants