Skip to content

perf: optimize scalar mul circuits - #1797

Open
yelhousni wants to merge 17 commits into
masterfrom
perf/zkgolf
Open

perf: optimize scalar mul circuits#1797
yelhousni wants to merge 17 commits into
masterfrom
perf/zkgolf

Conversation

@yelhousni

@yelhousni yelhousni commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR ports the scalar-multiplication optimizations developed for zk.golf challenges 5 and 6 into gnark's short-Weierstrass circuits. The main theme is to exploit compile-time constant bases aggressively and to reduce the cost of the remaining variable-base loops.

Optimizations

  • Add a signed-digit fixed-base comb for emulated G1 scalar multiplication. The scalar is recoded into odd signed digits, grouped into constant windows, and checked with one scalar-field identity. The top window folds the parity correction, so zero and even scalars are handled without a separate correction path.
  • Use incomplete additions inside the comb where the signed-window collision argument guarantees safety, then finish with a complete AddUnified tail for the cases that can cancel or double.
  • Carry the comb accumulator's Y coordinate implicitly through the incomplete chain. Consecutive slope hints are pinned by one deferred zero assertion and the final Y is materialized only once.
  • Split comb table selection into row and column one-hot selectors. R1CS keeps the wider default window (w=8), while SCS/PLONK uses a smaller window (w=5) to avoid paying too many constant-linear-combination gates.
  • Route Curve.ScalarMulBase through the comb for supported curves, making the fixed generator path complete and much cheaper than the previous variable-base fallback.
  • Route Curve.ScalarMul through the comb when the input point is a compile-time constant, finite, prime-order point. Non-subgroup or unsupported constant points fall back to the existing variable-base code.
  • Optimize complete JointScalarMulBase by computing the fixed-generator term with the comb, computing the variable-base term separately, and merging them with AddUnified.
  • Optimize MultiScalarMul by peeling off compile-time constant points and using comb scalar multiplication for those terms, while preserving the existing joint-scalar folding for the remaining variable points.
  • Add emulated.Field.AssertEvalIsZero, a zero-remainder variant of multivariate Eval, so slope constraints can avoid materializing an intermediate remainder witness and equality check.
  • Replace several division-based slope computations with hinted slopes certified by a single deferred zero assertion. This reduces AddUnified, tangent, chord, and incomplete loop slope checks without changing their exceptional-case semantics.
  • Carry Y implicitly in j-invariant-0 variable-base incomplete double-and-add loops, saving one materialization per loop step.
  • Port the fixed-base comb to emulated BN254 G2 and BLS12-381 G2. Constant G2 subgroup points now use precomputed Fp2 comb tables with the same odd recoding, parity fold, implicit-Y chain, and complete tail.
  • Port the fixed-base comb to native BLS12-377 G1 and G2. Native ScalarMulBase and constant-point ScalarMul now use complete fixed-base paths where supported.
  • Update scalar-mul stats to reflect the reduced G1 constraint counts.

Benchmarks

Benchmarks were run against master at 2ea1515eee1b1e4e8564ec0c1e15b7fd29bbb918 and this branch at the current working tree based on 655a64032. Each row compiles the same circuit once with r1cs.NewBuilder and scs.NewBuilder; the table reports GetNbConstraints() and GetNbInstructions(). The numbers were collected with temporary same-package benchmark harnesses that were removed after collection.

Harness command shape:

go test -run '^$' -bench '^BenchmarkZKGolf' -benchtime=1x -count=1 ./std/algebra/emulated/sw_emulated ./std/algebra/emulated/sw_bn254 ./std/algebra/emulated/sw_bls12381 ./std/algebra/native/sw_bls12377 ./std/evmprecompiles ./std/recursion/groth16 ./std/recursion/plonk

Emulated G1 methods

Method Backend Constraints before Constraints after Delta constraints Instructions before Instructions after Delta instructions
ScalarMulBase/complete r1cs 106,674 10,984 -95,690 (-89.7%) 119,788 11,735 -108,053 (-90.2%)
ScalarMulBase/complete scs 351,245 63,786 -287,459 (-81.8%) 364,359 64,957 -299,402 (-82.2%)
ScalarMulBase/incomplete r1cs 50,864 10,984 -39,880 (-78.4%) 55,885 11,735 -44,150 (-79.0%)
ScalarMulBase/incomplete scs 184,318 63,786 -120,532 (-65.4%) 189,339 64,957 -124,382 (-65.7%)
ScalarMul/constant/complete r1cs 106,674 10,984 -95,690 (-89.7%) 119,788 11,735 -108,053 (-90.2%)
ScalarMul/constant/complete scs 351,245 63,786 -287,459 (-81.8%) 364,359 64,957 -299,402 (-82.2%)
ScalarMul/constant/incomplete r1cs 50,864 10,984 -39,880 (-78.4%) 55,885 11,735 -44,150 (-79.0%)
ScalarMul/constant/incomplete scs 184,318 63,786 -120,532 (-65.4%) 189,339 64,957 -124,382 (-65.7%)
ScalarMul/variable/complete r1cs 107,622 91,710 -15,912 (-14.8%) 120,868 102,946 -17,922 (-14.8%)
ScalarMul/variable/complete scs 354,272 296,559 -57,713 (-16.3%) 367,518 307,795 -59,723 (-16.3%)
ScalarMul/variable/incomplete r1cs 51,029 40,583 -10,446 (-20.5%) 56,068 44,360 -11,708 (-20.9%)
ScalarMul/variable/incomplete scs 184,819 144,070 -40,749 (-22.0%) 189,858 147,847 -42,011 (-22.1%)
JointScalarMulBase/complete r1cs 206,747 101,764 -104,983 (-50.8%) 233,201 113,830 -119,371 (-51.2%)
JointScalarMulBase/complete scs 683,096 349,682 -333,414 (-48.8%) 709,550 362,168 -347,382 (-49.0%)
JointScalarMulBase/incomplete r1cs 90,619 72,160 -18,459 (-20.4%) 99,849 78,982 -20,867 (-20.9%)
JointScalarMulBase/incomplete scs 314,493 246,584 -67,909 (-21.6%) 323,723 253,406 -70,317 (-21.7%)
MultiScalarMul/mixed/complete r1cs 394,599 195,804 -198,795 (-50.4%) 447,593 220,007 -227,586 (-50.8%)
MultiScalarMul/mixed/complete scs 1,241,131 676,280 -564,851 (-45.5%) 1,294,125 701,323 -592,802 (-45.8%)
MultiScalarMul/mixed/incomplete r1cs 173,154 92,034 -81,120 (-46.8%) 191,627 100,398 -91,229 (-47.6%)
MultiScalarMul/mixed/incomplete scs 604,843 350,813 -254,030 (-42.0%) 623,316 360,017 -263,299 (-42.2%)

Emulated G2 methods

Method Backend Constraints before Constraints after Delta constraints Instructions before Instructions after Delta instructions
BN254 G2 ScalarMul/constant r1cs 215,732 19,648 -196,084 (-90.9%) 243,053 21,074 -221,979 (-91.3%)
BN254 G2 ScalarMul/constant scs 717,726 113,986 -603,740 (-84.1%) 745,047 116,191 -628,856 (-84.4%)
BLS12-381 G2 ScalarMul/constant r1cs 301,685 27,272 -274,413 (-91.0%) 338,813 29,246 -309,567 (-91.4%)
BLS12-381 G2 ScalarMul/constant scs 985,996 163,598 -822,398 (-83.4%) 1,023,124 166,655 -856,469 (-83.7%)

Native BLS12-377 methods

Method Backend Constraints before Constraints after Delta constraints Instructions before Instructions after Delta instructions
G1ScalarMulBase r1cs 1,101 930 -171 (-15.5%) 1,119 938 -181 (-16.2%)
G1ScalarMulBase scs 2,935 3,114 +179 (+6.1%) 2,953 3,122 +169 (+5.7%)
G1ScalarMul/constant r1cs 1,101 930 -171 (-15.5%) 1,119 938 -181 (-16.2%)
G1ScalarMul/constant scs 2,935 3,114 +179 (+6.1%) 2,953 3,122 +169 (+5.7%)
G2ScalarMulBase r1cs 3,983 1,702 -2,281 (-57.3%) 4,235 1,801 -2,434 (-57.5%)
G2ScalarMulBase scs 10,815 6,758 -4,057 (-37.5%) 11,067 6,857 -4,210 (-38.0%)
G2ScalarMul/constant r1cs 3,478 1,702 -1,776 (-51.1%) 3,768 1,801 -1,967 (-52.2%)
G2ScalarMul/constant scs 10,355 6,758 -3,597 (-34.7%) 10,645 6,857 -3,788 (-35.6%)

EVM precompile circuits

Circuit Backend Constraints before Constraints after Delta constraints Instructions before Instructions after Delta instructions
bn254_ecmul/bn254 r1cs 107,586 91,673 -15,913 (-14.8%) 120,828 102,905 -17,923 (-14.8%)
bn254_ecmul/bn254 scs 354,123 296,410 -57,713 (-16.3%) 367,365 307,642 -59,723 (-16.3%)
bn254_ecmul/bls12_377 r1cs 107,586 91,673 -15,913 (-14.8%) 120,828 102,905 -17,923 (-14.8%)
bn254_ecmul/bls12_377 scs 354,123 296,410 -57,713 (-16.3%) 367,365 307,642 -59,723 (-16.3%)
ecrecover/bn254 r1cs 210,668 105,626 -105,042 (-49.9%) 237,251 117,814 -119,437 (-50.3%)
ecrecover/bn254 scs 691,407 357,794 -333,613 (-48.3%) 717,990 370,402 -347,588 (-48.4%)
ecrecover/bls12_377 r1cs 210,668 105,626 -105,042 (-49.9%) 237,251 117,814 -119,437 (-50.3%)
ecrecover/bls12_377 scs 691,407 357,794 -333,613 (-48.3%) 717,990 370,402 -347,588 (-48.4%)
bls12381_g1_msm_2/bn254 r1cs 420,403 382,165 -38,238 (-9.1%) 473,891 430,087 -43,804 (-9.2%)
bls12381_g1_msm_2/bn254 scs 1,359,230 1,225,974 -133,256 (-9.8%) 1,412,718 1,273,896 -138,822 (-9.8%)
bls12381_g1_msm_2/bls12_377 r1cs 420,403 382,165 -38,238 (-9.1%) 473,891 430,087 -43,804 (-9.2%)
bls12381_g1_msm_2/bls12_377 scs 1,359,230 1,225,974 -133,256 (-9.8%) 1,412,718 1,273,896 -138,822 (-9.8%)

Recursion circuits

Circuit Backend Constraints before Constraints after Delta constraints Instructions before Instructions after Delta instructions
groth16/BW6_in_BN254_constant_vk r1cs 2,542,800 2,259,480 -283,320 (-11.1%) 2,887,581 2,562,005 -325,576 (-11.3%)
groth16/BW6_in_BN254_constant_vk scs 8,538,747 7,712,934 -825,813 (-9.7%) 8,883,528 8,016,943 -866,585 (-9.8%)
plonk/BLS12_377_in_BW6_761 r1cs 85,850 84,482 -1,368 (-1.6%) 87,406 85,958 -1,448 (-1.7%)
plonk/BLS12_377_in_BW6_761 scs 208,097 209,529 +1,432 (+0.7%) 210,070 211,422 +1,352 (+0.6%)
plonk/BLS12_381_in_BN254 r1cs 2,641,319 1,582,258 -1,059,061 (-40.1%) 3,021,783 1,797,673 -1,224,110 (-40.5%)
plonk/BLS12_381_in_BN254 scs 8,376,132 5,153,202 -3,222,930 (-38.5%) 8,756,596 5,373,025 -3,383,571 (-38.6%)
plonk/BW6_761_in_BN254 r1cs 7,112,724 4,254,945 -2,857,779 (-40.2%) 8,115,027 4,840,622 -3,274,405 (-40.3%)
plonk/BW6_761_in_BN254 scs 22,914,359 14,268,633 -8,645,726 (-37.7%) 23,916,662 14,866,182 -9,050,480 (-37.8%)

Notes:

  • Native BLS12-377 G1 under SCS grows by 179 constraints; R1CS improves and the native G2 paths improve on both backends.
  • The recursion benchmarks use representative fixed-verification-key verifier circuits, because those are the application paths affected by constant-base scalar multiplication.

Note

High Risk
Changes core elliptic-curve scalar multiplication and addition soundness paths (comb recoding, subgroup checks, slope hints) across emulated and native curves; incorrect constraints would break proof soundness for many circuits.

Overview
This PR cuts constraint cost for short-Weierstrass scalar multiplication by routing compile-time constant bases through a signed-digit fixed-base comb (precomputed windows, odd scalar recode, parity fold in the top window, implicit-Y incomplete chain, complete tail) and by cheaper slope checks in the remaining variable-base paths.

Emulated G1 (sw_emulated): new fixedbase.go powers ScalarMulBase, constant-point ScalarMul, complete JointScalarMulBase (comb + variable term + AddUnified), and MultiScalarMul peeling of constant points. Emulated G2 (BN254, BLS12-381) gets the same comb on constant subgroup points via fixedbase_g2.go and early dispatch in ScalarMul.

Native BLS12-377 adds G1/G2 fixed-base combs for ScalarMulBase and constant ScalarMul, with tests that reject malicious wrapped scalar recodes.

Supporting API: emulated.Field.AssertEvalIsZero, UnsafeFromLimbs, and ConstantValue; sw_emulated/slopes.go replaces many Div-based slopes with hinted slopes + deferred zero assertions; j=0 incomplete loops use implicit Y in double-and-add.

Stats in internal/stats/latest_stats.csv reflect large drops for fixed-base G1 scalar-mul benchmarks (e.g. BN254/secp256k1/P256).

Reviewed by Cursor Bugbot for commit 4e381d7. Bugbot is set up for automated code reviews on this repo. Configure here.

@yelhousni
yelhousni marked this pull request as draft July 24, 2026 17:23
@yelhousni
yelhousni requested a review from Copilot July 24, 2026 17:29

Copilot AI 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.

Pull request overview

Performance-focused update to gnark’s curve/arithmetic gadgets, primarily targeting proof-critical scalar multiplication and slope computation paths by introducing fixed-base signed-digit comb methods and cheaper deferred zero assertions for emulated-field polynomial checks.

Changes:

  • Added fixed-base signed-digit comb scalar multiplication with compile-time table generation and automatic dispatch for constant base points (native BLS12-377 and emulated SW curves, plus emulated BN254/BLS12-381 G2).
  • Introduced Field.AssertEvalIsZero and extended the multivariate evaluation hint to support “assert remainder is zero” mode (avoids materializing remainder witnesses).
  • Reworked emulated SW slope computations to use hinted witnesses certified by single deferred zero assertions (replacing many Div-based patterns) and added an implicit-y accumulator optimization in incomplete scalar-mul loops.

Reviewed changes

Copilot reviewed 24 out of 24 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
std/math/emulated/field.go Adds UnsafeFromLimbs and ConstantValue to support comb selection without limb range checks and enable constant-input specialization.
std/math/emulated/field_mul.go Adds AssertEvalIsZero and extends multivariate hint plumbing to optionally omit remainder outputs when asserting zero.
std/algebra/native/sw_bls12377/hints.go Registers the new comb recoding hint for native BLS12-377 fixed-base comb.
std/algebra/native/sw_bls12377/g2.go Adds constant-point comb dispatch for ScalarMul and comb-backed ScalarMulBase fast path.
std/algebra/native/sw_bls12377/g1.go Adds constant-point comb dispatch for ScalarMul (native G1).
std/algebra/native/sw_bls12377/fixedbase.go Implements native G1 fixed-base signed-digit comb with cached precomputed tables and recoding hint.
std/algebra/native/sw_bls12377/fixedbase_g2.go Implements native G2 fixed-base signed-digit comb with cached precomputed tables.
std/algebra/native/sw_bls12377/fixedbase_g2_test.go Adds correctness/count tests for native G2 comb-backed base multiplication.
std/algebra/native/sw_bls12377/fixedbase_count_test.go Adds constraint-count and correctness tests for native G1 comb-backed base multiplication.
std/algebra/emulated/sw_emulated/slopes.go Introduces hinted slope witnesses and implicit-y double-and-add helpers to reduce deferred-check cost.
std/algebra/emulated/sw_emulated/point.go Switches slope computations to asserted ratios/hinted slopes; adds comb dispatch for constant points and comb integration into MSM/joint scalar paths; adds implicit-y optimization.
std/algebra/emulated/sw_emulated/joint_comb_count_test.go Adds constraint-count tests for joint scalar multiplication modes and constant-point routing.
std/algebra/emulated/sw_emulated/hints.go Registers new slope/comb/implicit-chain hints used by the updated emulated SW implementation.
std/algebra/emulated/sw_emulated/fixedbase.go Implements emulated fixed-base signed-digit comb (table computation, selection, implicit-y chain, parity fold).
std/algebra/emulated/sw_emulated/fixedbase_test.go Adds extensive correctness and constraint-count coverage for emulated comb paths and constant-point MSM routing.
std/algebra/emulated/sw_bn254/hints.go Registers BN254 G2 comb recode/chain hints.
std/algebra/emulated/sw_bn254/g2.go Adds constant-point comb dispatch for BN254 emulated G2 ScalarMul.
std/algebra/emulated/sw_bn254/fixedbase_g2.go Implements BN254 emulated G2 fixed-base comb (tables, selection, implicit-y chain).
std/algebra/emulated/sw_bn254/fixedbase_g2_test.go Adds correctness/count tests for BN254 emulated G2 comb-backed scalar multiplication.
std/algebra/emulated/sw_bls12381/hints.go Registers BLS12-381 G2 comb recode/chain hints.
std/algebra/emulated/sw_bls12381/g2.go Adds constant-point comb dispatch for BLS12-381 emulated G2 ScalarMul.
std/algebra/emulated/sw_bls12381/fixedbase_g2.go Implements BLS12-381 emulated G2 fixed-base comb (tables, selection, implicit-y chain).
std/algebra/emulated/sw_bls12381/fixedbase_g2_test.go Adds correctness/count tests for BLS12-381 emulated G2 comb-backed scalar multiplication.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread std/algebra/native/sw_bls12377/fixedbase_g2_test.go Outdated
@yelhousni
yelhousni requested review from gbotrel and ivokub July 24, 2026 21:49
@yelhousni yelhousni self-assigned this Jul 24, 2026
@yelhousni yelhousni added dep: linea Issues affecting Linea downstream type: perf labels Jul 24, 2026
@yelhousni yelhousni added this to the v0.14.N milestone Jul 24, 2026
@yelhousni yelhousni changed the title perf: ... perf: optimize scalar mul circuits Jul 24, 2026
@yelhousni
yelhousni marked this pull request as ready for review July 24, 2026 22:03
@yelhousni
yelhousni requested a review from ThomasPiellard July 28, 2026 00:33

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bf770b2. Configure here.

Comment thread std/algebra/native/sw_bls12377/fixedbase.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dep: linea Issues affecting Linea downstream type: perf

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants