perf: share precomputed zeta-powers across ComputeEvaluationsAtZeta#13
Open
gbotrel wants to merge 2 commits into
Open
perf: share precomputed zeta-powers across ComputeEvaluationsAtZeta#13gbotrel wants to merge 2 commits into
gbotrel wants to merge 2 commits into
Conversation
…asks The Horner loop in EvaluateAtExt / ExtEvaluateAtExt runs sequentially inside each task: n ext-multiplies + n ext-adds + a bits.Reverse64 per row. Profiling showed this loop at 10–16% flat CPU on the synth bench (8.5s of CPU on tall) — second only to the Merkle-node permutation. Replace it with one SIMD-accelerated InnerProduct: precompute the bit-reversed powers of zeta once (shared across every task that evaluates an n-row polynomial at the same point), then the per-task inner work becomes: d.FFTInverse(_p, fft.DIF, ...) // unchanged res = ext.Vector(zPowBitRev).InnerProductByElement(_p) Both Vector.InnerProduct (ext × ext) and Vector.InnerProductByElement (ext × base) have AVX-512 paths in gnark-crypto. Powers are computed in parallel via chunked binary-exponentiation seeding (powUint64Ext) and permuted into bit-reversed order to match the FFTInverse DIF output, so EvaluateAtExtWithZPow needs no extra BitReverse pass. Numbers, 32-core EPYC 9R45, synth (100M cells), median of 3: shape before after speedup tall (2^22 × 24) 5.55 s 4.82 s 1.15× wide (2^16 × 1536) 1.25 s 1.20 s 1.04× Phase breakdown — evaluations-at-zeta: tall 450 ms → 149 ms 3.0× wide 57 ms → 7 ms 8.1× Tests: go test ./... + -race ./internal/poly ./prover. Existing prover end-to-end tests exercise Verify on every Prove; an off-by-one in the power-table order or a wrong-rail inner-product would surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR aims to improve prover performance by adding shared zeta-power evaluation and broadening parallelism across several prover hot paths. It also adds benchmarking/profiling infrastructure to measure phase-level performance.
Changes:
- Adds shared bit-reversed zeta-power tables and SIMD inner-product evaluation for evaluations at zeta.
- Introduces parallelism/capped FFT task plumbing in Merkle, FRI, RS commitment, quotient, and DEEP quotient paths.
- Adds phase callbacks, synthetic benchmark tooling, and optimisation planning documentation.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
prover/prover.go |
Reworks zeta evaluations, AIR quotient FFT task caps, DEEP quotient accumulation, and phase timing callbacks. |
prover/bench_deep_quotient_test.go |
Adds synthetic wide/tall prover benchmarks. |
plan_optims.md |
Adds optimisation roadmap and measurement guidance. |
internal/reedsolomon/rs.go |
Threads optional FFT options through RS encoding. |
internal/poly/utils.go |
Adds base-field binary exponentiation helper. |
internal/poly/utils_test.go |
Adds tests for PowUint64. |
internal/poly/ext.go |
Adds zeta-power table construction and WithZPow evaluation paths. |
internal/poly/compute_quotient.go |
Adds quotient parallel thresholds and parallel FFT/scaling loops. |
internal/parallel/parallel.go |
Adds task-budget and thresholded execution helpers. |
internal/merkle/tree.go |
Parallelizes internal-node tree construction by level. |
internal/fri/fri.go |
Parallelizes FRI folding and reuses parallel leaf hashing. |
internal/commitment/commitment.go |
Parallelizes RS encoding and exposes parallel leaf hashing helper. |
bench/synth/main.go |
Adds a synthetic proving benchmark/profiling command. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // zeta have been precomputed by the caller (typically once per (n, zeta) | ||
| // across many polynomials). Replaces the per-Horner ext-mul chain with a | ||
| // single SIMD InnerProductByElement on the FFTInverse output. | ||
| func EvaluateAtExtWithZPow(p Polynomial, d *fft.Domain, zPowBitRev []ext.E4, fftOpts ...fft.Option) ext.E4 { |
|
|
||
| ## House rules for the agent picking this up | ||
|
|
||
| - **One step = one PR**. Don't bundle. Each PR's review surface stays under ~300 LOC if possible. |
PR #13 review feedback (Copilot): the new bit-reversed power-table evaluators only had end-to-end coverage through the prover. Add internal/poly tests that build zPow and cross-check {Ext,}EvaluateAtExtWithZPow against the existing Horner evaluators across multiple sizes and zeta values (incl. identity / mixed coords).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Step 5 of `plan_optims.md`. The Horner loop in `EvaluateAtExt` / `ExtEvaluateAtExt` is sequential per task — n ext-mul + n ext-add + a `bits.Reverse64` per row. pprof showed it at 10–16% of total CPU on the synth bench, second only to the Merkle-node permutation.
Replace with one SIMD-accelerated inner product:
Powers are stored in bit-reversed-canonical order so the DIF FFT output can feed straight into the inner product — no extra `BitReverse` pass.
Numbers
32-core EPYC 9R45, synth bench at 100 M cells, median of 3:
`evaluations-at-zeta` phase:
The phase-level speedup overshoots prior estimates because Horner was paying for both ext-mul-chain and per-row `bits.Reverse64` indexing; the SIMD inner product retires both.
Test plan
🤖 Generated with Claude Code
Note
Medium Risk
Changes core polynomial evaluation math on the proving hot path; wrong power ordering would break proofs, but new tests parity-check against the prior Horner implementation.
Overview
Speeds up
evaluations-at-zetaby replacing the per-column Horner loop afterFFTInversewith a shared, bit-reversed ζ-power table and a single SIMD inner product (InnerProductByElement/InnerProduct).polygainsBuildZPowBitReversed(parallel O(n) build, bit-reversed order to match DIF output) andEvaluateAtExtWithZPow/ExtEvaluateAtExtWithZPow.ComputeEvaluationsAtZetacaches one table per distinct(n, evalPoint)and uses the fast path forn > 1, keeping the old evaluators only for length-1 polynomials.Tests assert the new path matches
EvaluateAtExt/ExtEvaluateAtExtacross sizes and ζ values.Reviewed by Cursor Bugbot for commit 9d8a728. Bugbot is set up for automated code reviews on this repo. Configure here.