Skip to content

perf(Mux): shared selector decomposition and ±Y symmetry optimization in emulated MSM#1704

Merged
yelhousni merged 16 commits intomasterfrom
perf/mux
Feb 18, 2026
Merged

perf(Mux): shared selector decomposition and ±Y symmetry optimization in emulated MSM#1704
yelhousni merged 16 commits intomasterfrom
perf/mux

Conversation

@yelhousni
Copy link
Copy Markdown
Contributor

@yelhousni yelhousni commented Feb 9, 2026

Description

Optimizes the Mux (multiplexer) function in the selector package and its usage across emulated field arithmetic and elliptic curve scalar multiplication.

The optimizations consist of three key improvements:

1. Share binary decomposition across emulated field limbs

Previously, emulated.Mux decomposed the selector into bits independently for each limb (e.g., 6 limbs × 4 bits = 24 redundant boolean assertions for a 16-to-1 Mux). Now we decompose once and reuse across all limbs.

  • Added BinaryMuxUnchecked and GeneralMuxUnchecked variants that skip boolean assertions when bits are already constrained (e.g., from bits.ToBinary).

Also:

  • Add constant selector folding: return inputs[sel] directly when sel is compile-time constant
  • Add n=2 specialization: use api.Select directly for 2-to-1 Mux

Files: std/math/emulated/field_ops.go, std/selector/mux.go and std/selector/multiplexer.go.

2. Exploit ±Y symmetry in MSM

In GLV-based scalar multiplication, the 16 precomputed points satisfy Y[i] = -Y[15-i]. This allows replacing a 16-to-1 Mux with an 8-to-1 Mux + conditional negation, saving constraints.

Added muxY8Signed helper that selects from 8 Y-values and conditionally negates based on the MSB.

Files: std/algebra/emulated/sw_emulated/point.go, std/algebra/emulated/sw_bls12381/g2.go

Type of change

  • New feature (non-breaking change which adds functionality)

How has this been tested?

  • All existing std/selector tests pass
  • All existing std/math/emulated tests pass
  • All existing std/algebra/emulated/sw_emulated tests pass
  • All existing std/algebra/emulated/sw_bls12381 tests pass
  • All existing std/evmprecompiles tests pass

How has this been benchmarked?

SCS constraint count comparison:

Single Scalar Multiplication

Circuit Before After Improvement
BLS12-381 G1.ScalarMul 291,552 279,576 -4.1%
BLS12-381 G2.ScalarMul 913,513 891,913 -2.4%

EVM Precompiles

Circuit Before After Improvement
BN254 ECMul 210,369 202,669 -3.7%
BLS12-381 G1MSM(10) 4,397,157 4,277,397 -2.7%
BLS12-381 G2MSM(10) 9,304,517 9,088,517 -2.3%
P256 Verify 666,146 652,482 -2.1%

Checklist:

  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • I did not modify files generated from templates
  • golangci-lint does not output errors locally
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Note

Medium Risk
Touches low-level constraint-building and mux selection logic used broadly across circuits; while changes are performance-oriented, mistakes could cause unsound or failing constraints across many call sites.

Overview
Speeds up constraint generation for multiplexers and emulated scalar multiplication by reducing redundant selector constraints and leveraging GLV point symmetry.

selector.Mux now short-circuits constant selectors, uses api.Select for 2-way muxes, and replaces the previous bounded-compare check with a builder-level MustBeLessOrEqCst on the already-decomposed selector bits. emulated.Field.Mux is reworked to decompose sel into bits once, reuse them across all limbs via selector.BinaryMux, and pad/check bounds only for non-power-of-two input counts.

Emulated GLV scalar-mul paths in sw_emulated/point.go and BLS12-381 G2 replace 16-to-1 Y-coordinate muxing with an 8-to-1 mux plus conditional negation (muxY8Signed/muxE2Y8Signed), reflecting the Y[i] = -Y[15-i] symmetry. Constraint builders (r1cs/scs) also skip adding trivially satisfied MustBeLessOrEqCst constraints when the compared bit is a constant zero; latest_stats.csv is updated accordingly.

Written by Cursor Bugbot for commit de2cef3. This will update automatically on new commits. Configure here.

@yelhousni yelhousni added this to the v0.14.N milestone Feb 9, 2026
@yelhousni yelhousni requested review from Copilot and ivokub February 9, 2026 15:44
@yelhousni yelhousni self-assigned this Feb 9, 2026
@yelhousni yelhousni added type: perf dep: linea Issues affecting Linea downstream labels Feb 9, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR optimizes selector multiplexing and its use in emulated field arithmetic and GLV-based scalar multiplication to reduce redundant constraints and improve performance.

Changes:

  • Add unchecked selector mux variants to reuse already-constrained selector bits.
  • Optimize selector.Mux and emulated.Field.Mux to avoid repeated binary decomposition work.
  • Exploit ±Y symmetry in GLV tables to replace 16-to-1 Y selection with 8-to-1 + conditional negation.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
std/selector/mux.go Adds BinaryMuxUnchecked / GeneralMuxUnchecked wrappers to skip redundant boolean assertions.
std/selector/multiplexer.go Adds constant-selector fast path, n=2 specialization, and an “unchecked” recursive path for non-power-of-2 muxes.
std/math/emulated/field_ops.go Reuses a single selector bit decomposition across all limbs and uses unchecked mux variants.
std/algebra/emulated/sw_emulated/point.go Adds muxY8Signed and switches GLV loops to 8-to-1 Y mux + conditional negation.
std/algebra/emulated/sw_bls12381/g2.go Adds muxE2Y8Signed for the same 8-to-1 + sign optimization in G2 GLV scalar mul.

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

Comment thread std/selector/multiplexer.go
Comment thread std/selector/multiplexer.go
Comment thread std/selector/multiplexer.go Outdated
Comment thread std/math/emulated/field_ops.go
Copy link
Copy Markdown
Collaborator

@ivokub ivokub left a comment

Choose a reason for hiding this comment

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

In general looks good. I can have a check if we can use MarkBoolean more efficiently to avoid redundant binary assertions.

Comment thread std/selector/mux.go Outdated
Comment thread std/selector/mux.go Outdated
Comment thread std/math/emulated/field_ops.go Outdated
Comment thread std/algebra/emulated/sw_bls12381/g2.go Outdated
Comment thread std/algebra/emulated/sw_bls12381/g2.go Outdated
Comment thread std/algebra/emulated/sw_emulated/point.go
Copy link
Copy Markdown

@cursor cursor bot left a comment

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.

Comment thread std/algebra/emulated/sw_emulated/point.go
@yelhousni yelhousni requested a review from ivokub February 17, 2026 21:30
Copy link
Copy Markdown
Collaborator

@ivokub ivokub left a comment

Choose a reason for hiding this comment

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

Thanks for the fixes! Looks good now!

@yelhousni yelhousni merged commit cbcf559 into master Feb 18, 2026
13 checks passed
@yelhousni yelhousni deleted the perf/mux branch February 18, 2026 20:00
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.

3 participants