Skip to content

perf(arithmetization): keccak optimization - #3727

Open
OlivierBBB wants to merge 6 commits into
mainfrom
perf/keccak-optimization
Open

perf(arithmetization): keccak optimization#3727
OlivierBBB wants to merge 6 commits into
mainfrom
perf/keccak-optimization

Conversation

@OlivierBBB

Copy link
Copy Markdown
Contributor

No description provided.

OlivierBBB and others added 2 commits August 5, 2026 14:59
…nction

kec_absorbing_phase was the most expensive keccak module: complexity 19987
(= sum of d^2 over its vanishing constraints) with PC_max 7.

Both figures came from its control flow rather than its arithmetic. A loop is
the only construct that survives the compiler's vectorisation pass as a distinct
PC value -- forward branches (if/else/switch/ternary) are predicated into a
single block, but back-edges are explicitly refused, since merging them would
unfold the loop indefinitely. Each surviving PC value then makes the vectoriser
duplicate the surrounding code into every block that can reach it: 55 lines of
source became ~660 micro-instructions spread over 7 blocks. On top of that,
nested loop and branch conditions multiply into the guard of every constraint
they enclose, raising its degree, and complexity weights degree quadratically.

Splitting the phase so that no function holds more than one loop removes both
effects. The five loops now live in kec_absorb_full_blocks,
kec_load_full_block, kec_absorb_final_block, kec_load_partial_words and
kec_zero_fill; kec_absorbing_phase itself becomes straight-line.

    module                    PC_max   limbs   lookups   complexity
    before                         7     439       330        19987
    after (sum of 6 modules)      <=2     357       295         3867

Whole-program function complexity drops from 97420 to 81300 (-17%), and the
worst PC_max in the program from 7 to 6. Trace cells for the region fall from
85698 to 16257; rows rise slightly, 207 to 234, but are far narrower.

Behaviour is unchanged. On a KECCAK_ACCEL=true workload (3 hashes, 288
keccak_f rounds) every module the split does not touch matches the parent
commit bit for bit, including value-dependent nonzero-cell counts: keccak_state
93640 lines / 1293622 nonzero, kec_theta 3168 / 738656, kec_squeezing_phase
3 / 100. Identical state contents and squeeze output mean identical hashes.
`zkc exec --check` on the same input exits 0, and `zkc format --check` is clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
Same treatment as the previous commit, applied to what was the second most
expensive keccak module: complexity 9032 with PC_max 3.

kec_theta held two sequential loops -- the D[x] computation and the row mixing
-- after a straight-line block computing the column parities C[x]. Two
back-edges mean two extra PC values on top of the entry block, and the
vectoriser duplicates code into each of them.

Moving each loop into its own function leaves kec_theta straight-line:

    module                    PC_max   complexity
    kec_theta (before)             3         9032
    kec_theta (after)              1          492
    kec_theta_d                    2          779
    kec_theta_mix_rows             2         2396
    sum (after)                  <=2         3667

Whole-program function complexity drops from 81300 to 75935 (-6.6%), for a
cumulative 97420 -> 75935 (-22%) across both splits. Trace cells for the theta
region fall from 1783584 to 477792; rows rise from 3168 to 3744.

Behaviour is unchanged, verified as in the previous commit: on a
KECCAK_ACCEL=true workload every untouched module matches bit for bit,
including keccak_state (93640 lines / 1293622 nonzero), kec_chi, kec_rho,
kec_pi, kec_iota, keccak_f, keccak_f_round and kec_squeezing_phase.
`zkc exec --check` exits 0 and `zkc format --check` is clean.

Note that no PR-gating CI job currently executes this code: the keccak check in
arithmetization-simple-zkc-binary-run-and-go-corset-check.yml builds with
KECCAK_ACCEL=false, so keccak runs in guest software rather than through this
library. Tracked separately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
Copilot AI lite review requested due to automatic review settings August 5, 2026 15:17
@OlivierBBB OlivierBBB linked an issue Aug 5, 2026 that may be closed by this pull request
@OlivierBBB OlivierBBB changed the title Perf/keccak optimization perf(arithmetization): keccak optimization Aug 5, 2026
@OlivierBBB
OlivierBBB requested review from letypequividelespoubelles and removed request for Copilot August 5, 2026 15:17

@letypequividelespoubelles letypequividelespoubelles left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Lgtm,

Nice comment about perf.

We have an action that tests tracing performance for zkc-keccak, you can put as many keccak rounds you want.

Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
No PR check currently executes arithmetization/src/main/lib/keccak. The existing
"Trace Keccak rust and check it" step runs keccak-rust-exec, and KECCAK_ACCEL is
only consulted on the zig build path in vector-build -- the rust path
(cargo rustc) has no -Dkeccak-accel equivalent. So that step hashes in guest
software and never reaches the zkc keccak library. Confirmed empirically: with
the committed keccak input, every kec_* module reports 0 rows in
`zkc trace --stats`.

Only the weekly metrics workflow and the manual keccak-vs-reference benchmark
exercise the library today, so library changes can merge with no automated
verification -- as the two preceding commits on this branch did.

Adds a parallel step using keccak-zig-exec with KECCAK_ACCEL=true, mirroring the
rust step: 1 vector, ZKC_EXEC_FLAGS=--check so constraints are validated and not
just execution, its own KECCAK_JSON_FILE so the two steps cannot clash, and
ZKC_REF="" to avoid reinstalling zkc.

Costs no new tooling: zig is already installed by setup-arithmetization-riscv
via setup-zig. Measured locally at 52.6s wall including the zig build and a zkc
reinstall, against the 15-minute timeout inherited from the rust step.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Olivier Bégassat <olivier.begassat.cours@gmail.com>
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.

perf(arithmetization): keccak optimization

2 participants