Skip to content

Ristretto Precomputed MSM Switcher - #1007

Open
Daeinar wants to merge 1 commit into
mainfrom
philipp/mixed-msm
Open

Ristretto Precomputed MSM Switcher#1007
Daeinar wants to merge 1 commit into
mainfrom
philipp/mixed-msm

Conversation

@Daeinar

@Daeinar Daeinar commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator

Follow-up to the review of #989 (#989 (comment)): the decision whether a mixed MSM should use the precomputed tables or a plain MSM moves from the caller into RistrettoPrecomputation.

What changes

  • RistrettoPrecomputation stores the static points next to dalek's tables (dalek exposes no way to recover them). mixed_multi_scalar_mul runs Straus over the tables while static + DYNAMIC_POINT_WEIGHT * dynamic <= MAX_STRAUS_POINTS (3, 600) and otherwise one plain MSM over the chained static and dynamic inputs, with no concatenation.
  • MixedMultiScalarMul::mixed_multi_scalar_mul doc: the implementation falls back itself; callers need not choose.
  • test_precomputed_multiscalar_mul covers the fallback in both directions (static-heavy and dynamic-heavy) with the same length checks.
  • New benches/mixed_msm.rs times the two dalek primitives over a grid of static sizes (powers of two and midpoints, 64..2048) and dynamic sizes (0, 32, 128, 512); it is how the constants were fitted and how they would be retuned on another machine.

Why a weighted rule

Straus over the tables is linear in the static count with a flat per-point cost, Pippenger's per-point cost falls with size, and the crossover sits far below what operation counts predict (the 7.7 KB per-point tables fall out of cache). Dynamic points shift the crossover: a dynamic point costs a per-call table and projective additions, about three static points' worth. Measured crossovers on an M2 Max with dense scalars: ~600 static points at D = 0, ~490 at D = 32, ~245 at D = 128; at D = 512 the plain MSM wins at every static size. Least-squares fit 589 - 2.7 D, rounded to the two constants. Near the crossover the two paths are within a few percent of each other, so a threshold misplaced by ~50 points costs under 1%.

A simpler design, deciding at precompute by static count alone and skipping the tables above it, regressed BP++ 64-bit x32 proving by 5.5%: its static-only MSMs still profit from the tables at 521 points while the verifier's mixed call does not.

Impact

BP++ (#989) keeps the decisions its hand-written fallback made (tables for all its provers and for verifiers up to 64-bit x16, plain MSM for the 64-bit x32 verifier) and will drop its own fallback arm once this lands. No other callers of the precomputation exist yet.

🤖 Generated with Claude Code

`RistrettoPrecomputation` keeps the static points next to the tables and
chooses per call: Straus over the tables while
`static + 3 * dynamic <= 600`, otherwise one plain MSM over the chained
static and dynamic inputs, so callers no longer fall back themselves.
The bound is a heuristic fitted on an (S, D) grid with
`benches/mixed_msm.rs` (dalek primitives timed directly, M2 Max): the
crossover lies at about 600 static points with no dynamic ones, 490 with
32 and 245 with 128, and with 512 dynamic points the plain MSM wins at
every static size.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@Daeinar
Daeinar requested a review from jonas-lj August 21, 2026 12:11
@Daeinar Daeinar self-assigned this Aug 21, 2026
@Daeinar

Daeinar commented Aug 21, 2026

Copy link
Copy Markdown
Collaborator Author

@jonas-lj 👆 Ready for your review. Thanks!

@Daeinar Daeinar changed the title Choose Straus or a plain MSM inside the Ristretto precomputation Ristretto Precomputed MSM Switcher Aug 21, 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.

1 participant