`perf(poseidon): FFT MDS in AIR + RATE=12 MMO sponge#216
Open
Barnadrot wants to merge 5 commits intoleanEthereum:mainfrom
Open
`perf(poseidon): FFT MDS in AIR + RATE=12 MMO sponge#216Barnadrot wants to merge 5 commits intoleanEthereum:mainfrom
Barnadrot wants to merge 5 commits intoleanEthereum:mainfrom
Conversation
The Poseidon AIR constraint folder evaluates mds_air_16 8x per row across runtime types (F, EF, FPacking, EFPacking). Previously this used Karatsuba convolution (72 mults). Switch to the same FFT-MDS already used in the permute_simd hot path: DIT_FFT(lambda/16 ⊙ DIF_IFFT(state)), 50 mults. Saves 22 mults × 8 MDS calls per AIR row = 176 mults/row, ~10% reduction in AIR Poseidon eval mult count. AIR Poseidon eval is ~10% of CPU time in the e2e prover (eval_2_full_rounds_16 + eval_last_2_full_rounds_16 + Poseidon16Precompile::eval). The unpacked lambda_over_16 = (DIF_IFFT(MDS_CIRC_COL) * 16^-1) is factored out of the SimdPrecomputed branch and stored at the top of Precomputed; the SIMD branch reuses it (no duplication). FFT helpers (bt/dit/neg_dif/dif_ifft/dit_fft) are ungated from target_feature since they're pure generic Rust, and their bound is relaxed from Algebra<KoalaBear> to PrimeCharacteristicRing + Mul<KoalaBear> to match mds_circ_16 (so EFPacking, which lacks Algebra<KoalaBear>, is admitted). Predicted magnitude: medium (1.0-1.5%).
Reduce Poseidon permutations per Merkle leaf by 22-32% by increasing the sponge absorption rate from 8 to 12 field elements per permutation call. Changes: - sponge.rs: relax RATE==OUT and WIDTH==OUT+RATE asserts, support arbitrary RATE - merkle.rs: SPONGE_RATE=12, padded_full_base_width helper, corrected n_zero_suffix_rate_chunks formula for RATE!=WIDTH/2 - verifier.rs: pad base_data to sponge-aligned length before hashing - hashing.py: zk-DSL slice_hash_rtl rewritten for RATE=12, @inline removed to fix conditional branch fall-through bug
Replace standard outer-sponge with Matyas-Meyer-Oseas (MMO) feedforward construction. Same Poseidon-16 permutation, same RATE=12, but collision security lifts from 62-bit to 124-bit by chaining the full 16-element state instead of just the 4-element capacity. Changes: - sponge.rs: mmo_hash_slice, mmo_hash_rtl_iter, mmo_precompute_zero_suffix_state with full-state feedforward (XOR pre-perm state into post-perm state) - merkle.rs: wire MMO hash functions into Merkle tree construction - verifier.rs: use MMO hash in verification path - poseidon_16: new poseidon16_permute precompile (16-element output) for zk-DSL recursive verifier, with AIR constraints and trace generation - hashing.py: zk-DSL updated to use MMO via poseidon16_permute precompile Security: standard sponge collision = c*log2(p)/2 = 62 bits (unshippable). MMO collision = b-bit birthday on full state output = 124 bits (meets target). Verified against: Coratger-Khovratovich-Wagner-Mennink 2026, SAFE proof (eprint 2023/520), Beetle (CHES 2018).
Under the workspace default thin LTO profile, the new RATE=12 + MMO sponge code introduced cross-crate calls that did not get inlined: mmo_hash_slice, mmo_precompute_zero_suffix_state, compress_mut, permute_mut. The hot loop in build_merkle_tree_koalabear ended up making out-of-line calls into mt_symetric and mt_koala_bear on every absorb, spilling the 16-element state to the stack each iteration. Adding #[inline] makes these functions available for cross-CGU inlining under thin LTO, matching the codegen fat LTO already produces. No semantic change. The functions are short hot-path wrappers/loops that the compiler should inline anyway given the chance.
- rustfmt: re-flow long lines introduced by the MMO commit - clippy: replace redundant closures in sponge tests with function refs - clippy: allow too_many_arguments on eval_last_2_full_rounds_16 (AIR helper, 9 args) - clippy: rewrite full_output_flags loop with .iter().enumerate()
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.
Title
perf(poseidon): FFT MDS in AIR + RATE=12 MMO sponge — -5.6% on XMSS aggregationBody
Summary
Three perf changes to the Poseidon path on the leaf-aggregation hot path, plus a small commit adding
#[inline]to four cross-crate hot functions. Net -5.58% wall-clock on the production XMSS leaf workload (1550 signatures,log_inv_rate=1).b11aac3amds_air_16: Karatsuba (72 mults) → FFT MDS (50 mults)273190442198c0b4602859ad#[inline]tommo_hash_slice,mmo_precompute_zero_suffix_state,compress_mut,permute_mutBenchmark
Hetzner AX42-U (Zen 4),
RUSTFLAGS="-C target-cpu=native", 1550 signatures,log_inv_rate=1. zk-alloc allocator (workspace default). Production release profile (fat LTO,codegen-units = 1). Warm-proof average over 4 consecutive proofs after a discarded cold warmup; per-proof variance was <1% on both branches.main(19f1c774)perf/poseidon-fft-mmoWelch's t-test on individual warm proof times: t = -28.8, df ≈ 6, p < 1e-6.
Reproduce with the production profile (fat LTO +
codegen-units = 1):CARGO_PROFILE_RELEASE_LTO=fat \ CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1 \ RUSTFLAGS="-C target-cpu=native" \ cargo run --release -- xmss --n-signatures 1550 --log-inv-rate 1Proof size grows 338 → 345 KiB (+2.0%) because the recursive zk-DSL verifier program adds dispatch logic for RATE=12 (250,208 → 253,755 instructions in the aggregation program). The wall-clock improvement is the net gain after that overhead.
Per-commit attribution
Each commit was cherry-picked onto
mainand benchmarked individually under the production profile:#[inline]annotations aloneCorrectness
All five integration tests pass at HEAD:
test_run_whirtest_xmss_signaturetest_type_1_aggregationtest_aggregationtest_type_2_aggregationEnd-to-end verification (including the recursive zkVM verifier) succeeds in ~37 ms. Proof remains valid under the existing verifier.
Security — RATE=12 + MMO
RATE=8 with capacity=8 in a plain Sponge gives 128-bit generic collision security (capacity/2). Bumping to RATE=12 with capacity=4 in a plain Sponge would drop generic collision security to ~64 bits, which is unacceptable.
Commit
2198c0b4swaps the absorption mode from plain Sponge to MMO (Matyas-Meyer-Oseas) feedforward: each absorb step XORs the input back into the state after the permutation. MMO with width-16 KoalaBear gives ~124-bit collision security at RATE=12, capacity=4. Full analysis in the commit message.Why the
#[inline]commit is includedAlthough the production profile uses fat LTO and inlines the new sponge calls aggressively, the workspace
[profile.release]islto = "thin". Under thin LTO, the new RATE=12 + MMO hot path crosses three crates —mt_whir::merkle::build_merkle_tree_koalabear→mt_symetric::sponge::mmo_hash_slice→Compression::compress_mut(impl onPoseidon1KoalaBear16inmt_koala_bear) →Permutation::permute_mut— and these calls are left out-of-line. Inside the rayon worker loop that means a stack spill of the full 16-element packed state on every absorb iteration, which dominates per-iteration cost.Concretely, without the
#[inline]commit, the same source under workspace defaults (cargo run --release -- xmss ...) regresses +3.2% vsmain. With it, the same command improves -4.87% vsmainon the same machine.#[inline]is just a hint; under fat LTO the compiler already inlines these. The annotations only change codegen under thin LTO, where they let it match what fat LTO already produces. No semantic change.Files touched
crates/backend/koala-bear/src/poseidon1_koalabear_16.rs— FFT MDS,#[inline]crates/lean_vm/src/tables/poseidon_16/mod.rs— FFT MDScrates/backend/symetric/src/sponge.rs— RATE=12, MMO mode,#[inline]crates/backend/symetric/src/permutation.rs—#[inline]crates/whir/src/merkle.rs— sponge integration, padding formulacrates/backend/fiat-shamir/src/verifier.rs— sponge integrationcrates/rec_aggregation/zkdsl_implem/hashing.py— zk-DSL RATE=12 portTest plan
cargo test --workspace --release