Skip to content

GKR performance: tracking issue #52

Open

Description

@xrvdg

Overview of the performance work on the GKR prover: gkr_reduce in the prover and verifier, and crates/gkr. Part of #41.

Benchmark

cargo bench -p prover --bench prover -- gkr, shape (10, 15), M3 Pro (5 performance + 6 efficiency cores).

State All threads One thread
Before #55 363 ms 480 ms
#55 166 ms 283 ms
main 115 ms 215 ms
#146 (draft) 68 ms 158 ms

All numbers are medians. The last two rows were measured on 2026-10-22. For one thread, #55 used single_threaded.patch and the later rows RAYON_NUM_THREADS=1.

Done

  • Grand product GKR: core structure #40: Core structure of the grand product GKR.
  • GKR integration #49: GKR integrated into the prover and verifier. Removed the deallocations and the deque from the prove layer. Replaced the m-table with a factored claim discharged through the PCS.
  • GKR: benchmark and profiling setup + improvements #55: Benchmark and profiling setup: divan bench, profiling example, and single_threaded.patch. Also:
    • transposed the bit table before leaf construction, so the walk is sequential;
    • fixed the exponential over-allocation in SuffixTable::new;
    • made the m-table accumulation branchless;
    • zeroed vectors with bytemuck's zeroed_vec.
  • GKR: Second optimisation set #60: Second pass over the round loop:
    • SuffixTable flattened into one preallocated buffer, reused across layers;
    • batched_eval consumes the circuit instead of cloning the leaves;
    • eq_factor inlined in closed form;
    • rayon fold replaced by 2-wide interleaved PMULL chains, with a fused multiply-reduce in mul3_wide;
    • layer sumcheck chunked to reduce variance.

No longer applies

In progress

To do

Follow-ups to #146

At (10, 15) on all threads, about 25 of the 68 ms run on a single thread. Removing that would bring the bench to about 43 ms.

  • Eq tables: EqTables::new takes about 18.5 ms, all of it serial.
    • Don't build the top table, which round 1 of the leaves and of layer 1 never reads (about 7 ms).
    • Parallelise the doubling.
    • Or split the table (Split Suffix Table optimisation #58), which makes both of the above moot.
  • Zeroing with calloc runs on one thread (about 4.3 ms): eq_memory, bound_children and layer 3 are fully written before they are read. This is what's left of the old "allocation-free intermediate layers" item.
  • Parallel bit transpose in GrandProductCircuit::new (about 2.1 ms).
  • Don't split the dense round loops below 2^15 lanes (about 0.45 ms per proof).
  • Fuse bind with the next round's sums (Fuse GKR dense folding with the next round message pass #109). round_sums and bind are bound by memory bandwidth.
  • Construction (at most about 1 ms): fold layer 4 in layer 3's pass, or don't materialise layer 3 at all.

Ports from the BitZ-PoC (audit of 2026-09-23)

Each issue owns its own scope. Dependencies are in parentheses.

Related

Activity

  1. xrvdg commented on Sep 11, 2026

    @xrvdg
    CollaboratorAuthor

    #55

    ┌───────────────────────────┬──────────┬──────────┬─────────┐
    │                           │  before  │  after   │ speedup │
    ├───────────────────────────┼──────────┼──────────┼─────────┤
    │ parallel (default)        │ 363.4 ms │ 166.1 ms │ 2.19x   │
    ├───────────────────────────┼──────────┼──────────┼─────────┤
    │ single-threaded (patched) │ 480.3 ms │ 283.4 ms │ 1.70x   │
    └───────────────────────────┴──────────┴──────────┴─────────┘
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions