Skip to content

perf(ferret): ~5.7x faster COT extension (consistency check) - #410

Closed
heeckhau wants to merge 3 commits into
devfrom
perf/ferret-extension
Closed

perf(ferret): ~5.7x faster COT extension (consistency check)#410
heeckhau wants to merge 3 commits into
devfrom
perf/ferret-extension

Conversation

@heeckhau

@heeckhau heeckhau commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Speeds up the Ferret COT extension by attacking its real bottleneck — the
SPCOT consistency check (~90% of per-side cost in the Regular variant). All
changes are variant-agnostic (they live in the SPCOT check + core
primitives), so they benefit both Regular and Uniform.

Regular LPN — per-side compute (profiler, rayon)

n baseline this PR speedup check alone
256K 5.2 ms 2.0 ms 2.6× 3.6 → 1.4
1.0M 17.5 ms 3.6 ms 4.9× 14.4 → 1.9
4.1M 64.7 ms 11.3 ms 5.7× 57.4 → 4.8 (12×)

(The end-to-end ferret_sender criterion bench — which runs the default Uniform
config — went 1.024 s → 193 ms, 5.3×; the optimized phase is the same one
that dominates Regular.)

The bottleneck

Profiling at production params showed the consistency check, not the LPN encode,
was the cost (the encode is ~1%). The check was a sequential, latency-bound
clmul inner product
over the GGM-leaf vector plus a single-threaded chi PRG
fill — neither parallelized.

Changes (variant-agnostic)

  • core/block: inn_prdt_no_red uses 8 independent accumulators (breaks the
    clmul latency chain); inn_prdt_red gains a rayon-parallel chunked path.
    Result is identical (the reduction is GF(2)-linear over the XOR-combine).
  • core/prg: random_blocks_par (parallel, byte-identical counter-mode
    fill), chi_inner_product (regenerates chi on the fly inside the inner
    product, so the large chi vector is never materialized), and blocks_at
    (seekable single-block gather). The SPCOT check on both parties uses these —
    the receiver stores a 16-byte seed instead of the full chi vector.
  • core/ggm: GgmTree takes a caller-provided scratch buffer for its
    internal nodes, reused per-thread in the SPCOT extend loops.
  • ferret/spcot: wires the above into the consistency check and the
    GGM generation.

Correctness

All existing protocol tests pass in both rayon and non-rayon configs
(ferret::tests::test_ferret for Regular + Uniform, plus SPCOT/MPCOT/cuckoo).
New unit tests assert the new primitives match the explicit references
(random_blocks_par/blocks_at vs the sequential PRG stream; chi_inner_product
vs inn_prdt_red(&explicit_chi, b)). The protocol's own check
(hashed_v == hashed_w) would fail on any sender/receiver divergence.

Notes

  • Scope: deliberately limited to the variant-agnostic consistency-check /
    core-primitive work. Earlier revisions also optimized the Uniform-only cuckoo
    path (batched AES, u32 indices, parallel combine) — dropped, since you're
    removing Uniform and those would be dead code.
  • No behavior change, rayon on or off (sequential fallbacks are
    byte-identical).
  • Profiler harness (ferret::profile, an #[ignore]d test) is included for
    reproducibility; happy to drop it.
  • Numbers from an Apple Silicon (ARM) machine, 18 cores. The 8-accumulator trick
    helps even more on x86 (PCLMULQDQ latency).

Possible follow-ups (not here)

  • Not materializing the SPCOT vector (fuse generation with the check) —
    further memory win, ~1.4× more on the Uniform path; on Regular the vector is
    already small so the benefit is mostly memory.
  • x86 VPCLMULQDQ backend in clmul for the inner product on x86 servers.

@sinui0

sinui0 commented Jun 8, 2026

Copy link
Copy Markdown
Collaborator

Is this speedup for the uniform variant? We're going to remove that, we only use regular

heeckhau added 3 commits June 8, 2026 11:49
- inn_prdt_no_red: 8 independent accumulators to break the clmul latency
  chain; inn_prdt_red gains a rayon-parallel chunked path (identical result).
- prg: add random_blocks_par (parallel, byte-identical counter-mode fill),
  chi_inner_product (regenerates chi on the fly inside the inner product), and
  blocks_at (seekable single-block gather), with equivalence tests.
- ggm: GgmTree takes a caller-provided scratch buffer for its internal nodes,
  so it can be reused across trees instead of reallocated per call.
The consistency check was the dominant cost. Its chi vector is now regenerated
on the fly inside a parallel inner product (no 294 MB materialization; the
receiver keeps a 16-byte seed and uses blocks_at for the alpha-indices), and the
extend loops reuse a per-thread GGM scratch buffer.
Drives the real phase functions at production LPN params and reports per-phase
timings; compares Uniform vs Regular. Run with
`cargo test -p mpz-ot-core --release --features rayon ferret::profile -- --ignored --nocapture`.
@heeckhau
heeckhau force-pushed the perf/ferret-extension branch from a596b6a to 96e76cc Compare June 8, 2026 09:49
@heeckhau heeckhau changed the title perf(ferret): ~5x faster COT extension perf(ferret): ~5.7x faster COT extension (consistency check) Jun 8, 2026
@heeckhau

heeckhau commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

I have removed all improvements for "uniform" and only kept those that target "regular"

@sinui0

sinui0 commented Jun 11, 2026

Copy link
Copy Markdown
Collaborator

Superceded by #425

@sinui0 sinui0 closed this Jun 11, 2026
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