Skip to content

perf: rust backed PTC sampling - #9263

Open
guha-rahul wants to merge 5 commits into
ChainSafe:unstablefrom
guha-rahul:compute_rust_ptc
Open

perf: rust backed PTC sampling#9263
guha-rahul wants to merge 5 commits into
ChainSafe:unstablefrom
guha-rahul:compute_rust_ptc

Conversation

@guha-rahul

@guha-rahul guha-rahul commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Motivation

#9013 and https://github.com/ChainSafe/lodestar/pull/9211/changes/BASE..27a20754340c6fe83a3822f3ba219ac39c3c1efa#r3074070771 highlight that computing ptc sampling is costly. T he sanity tests needed a 30s → 60s timeout bump. This PR moves the resource intensive into Rust

Description

Performance

 computePayloadTimelinessCommitteeForSlot - pure TS vs Rust magic (250k-1M validators)
    ✔ naive TS - naiveComputePayloadTimelinessCommitteeForSlot - 25000    745.4242 ops/s    1.341518 ms/op   x0.963        469 runs   1.45 s
    ✔ (uses native Rust) -computePayloadTimelinessCommitteeForSlot - 2    3711.374 ops/s    269.4420 us/op   x1.121       1229 runs  0.993 s
    ✔ naive TS - naiveComputePayloadTimelinessCommitteeForSlot - 10000    519.5736 ops/s    1.924655 ms/op   x1.230        211 runs   1.12 s
    ✔ (uses native Rust) -computePayloadTimelinessCommitteeForSlot - 1    1230.998 ops/s    812.3490 us/op   x1.286        535 runs   1.11 s

  computePayloadTimelinessCommitteesForEpoch - pure TS vs  Rust magic (250k -1M validators)
    ✔ naive TS - naiveComputePayloadTimelinessCommitteesForEpoch - 250    26.56509 ops/s    37.64339 ms/op   x1.035         10 runs  0.907 s
    ✔ (uses native Rust) -computePayloadTimelinessCommitteesForEpoch -    1063.618 ops/s    940.1870 us/op   x1.339        605 runs   1.48 s
    ✔ naive TS - naiveComputePayloadTimelinessCommitteesForEpoch - 100    25.44836 ops/s    39.29526 ms/op   x1.071         10 runs  0.937 s
    ✔ (uses native Rust) -computePayloadTimelinessCommitteesForEpoch -    1119.058 ops/s    893.6090 us/op   x1.251       1025 runs   1.99 s

AI Assistance Disclosure

Claude code was used for perf harness and wiring.

@guha-rahul
guha-rahul requested a review from a team as a code owner April 23, 2026 16:30

@gemini-code-assist gemini-code-assist Bot 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.

Code Review

This pull request introduces optimized, Rust-backed implementations for Payload Timeliness Committee (PTC) computations by integrating new functions from the @chainsafe/swap-or-not-shuffle package. The existing JS implementations have been renamed to "naive" versions and are now used for verification in new performance benchmarks. Feedback includes concerns regarding the use of a local tarball dependency which breaks cross-platform support and CI builds, a suggestion to use the assert utility for error handling consistency, and a request to remove debug logging from the test suite.

"@chainsafe/pubkey-index-map": "^3.0.0",
"@chainsafe/ssz": "^1.4.0",
"@chainsafe/swap-or-not-shuffle": "^1.2.1",
"@chainsafe/swap-or-not-shuffle": "file:../../chainsafe-swap-or-not-shuffle-1.2.1-local.tgz",

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.

high

The use of a local tarball dependency (file:../../...) is problematic for shared repositories as it breaks builds for other developers and CI environments that do not have the file at that specific relative path. Furthermore, the pnpm-lock.yaml changes indicate that several supported platforms (e.g., darwin-x64, linux-x64-gnu) have been removed and versions for native bindings have been downgraded to 0.0.2. This dependency should be properly published to a registry or integrated via workspace references to maintain repository integrity and cross-platform support.

Comment on lines +326 to +328
if (shuffling.length === 0) {
throw Error("Validator indices must not be empty");
}

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.

medium

Use the assert utility for consistency with other validation checks in this file and the Lodestar codebase.

  assert(shuffling.length > 0, "Validator indices must not be empty");

Comment on lines +127 to +128
// eslint-disable-next-line no-console
console.log(`[vc=${vc}] effectiveBalanceIncrements[0]=${effectiveBalanceIncrements[0]}`);

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.

medium

Remove debug console.log and the associated eslint disable comment before merging.

Comment on lines +128 to +151
console.log(`[vc=${vc}] effectiveBalanceIncrements[0]=${effectiveBalanceIncrements[0]}`);

const naiveResult = naiveComputePayloadTimelinessCommitteesForEpoch(
cachedState,
epoch,
epochCtx.currentShuffling.committees,
effectiveBalanceIncrements
);
const rustResult = computePayloadTimelinessCommitteesForEpoch(
cachedState,
epoch,
epochCtx.currentShuffling,
effectiveBalanceIncrements
);
for (let i = 0; i < naiveResult.length; i++) {
const naive = naiveResult[i];
const rust = rustResult[i];
if (naive.length !== rust.length) {
throw new Error(`PTC length mismatch at slot ${i} (vc=${vc}): naive=${naive.length} rust=${rust.length}`);
}
for (let j = 0; j < naive.length; j++) {
if (naive[j] !== rust[j]) {
throw new Error(
`PTC index mismatch at slot ${i} position ${j} (vc=${vc}): naive=${naive[j]} rust=${rust[j]}`

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this should be removed, added this just to check the values from rust and native values

nazarhussain added a commit to ChainSafe/lodestar-z that referenced this pull request Aug 13, 2026
## Motivation

Preparation for porting the full
[`@chainsafe/swap-or-not-shuffle`](https://github.com/ChainSafe/swap-or-not-shuffle)
API surface (needed by ChainSafe/lodestar#9263) into lodestar-z. The
shuffle algorithm gets a standalone module as its canonical home so
`state_transition` and the JS bindings share a single copy.

## Description

**Pure mechanical move — no code change.**
`src/state_transition/utils/shuffle.zig` →
`src/swap_or_not_shuffle/root.zig` (git detects a 100% rename; the file
is byte-identical).

Everything else is wiring:
- Register the `swap_or_not_shuffle` module + its test step in
`build.zig.zon`; add it to `state_transition` and bindings-library
imports
- Update the three import sites (`epoch_shuffling.zig`,
`bindings/napi/shuffle.zig`, `utils_test_root.zig`) and drop the
now-unused `shuffle` re-export from `state_transition/root.zig`
- Add `zig build test:swap_or_not_shuffle` to CI

A follow-up PR (#stacked on this branch) ports the reference-parity API
+ JS binding.

## Validation

`zig fmt --check`, `test:swap_or_not_shuffle`, `test:state_transition`,
`build-lib:bindings`, and the existing `bindings/test/shuffle.test.ts`
all pass.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
nazarhussain added a commit to ChainSafe/lodestar-z that referenced this pull request Aug 13, 2026
Port the full @chainsafe/swap-or-not-shuffle surface into the module
(exact validation order and errors, ComputeShuffledIndex, proposer and
sync committee index functions) and expose it as bindings.swapOrNotShuffle
with reference-identical names, semantics, and error messages.

Differential tests compare every function against the npm package;
2M-index sweeps show zero divergence. Perf benches show equal or faster
results than the Rust binding. Needed for ChainSafe/lodestar#9263.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
nazarhussain added a commit to ChainSafe/lodestar-z that referenced this pull request Aug 19, 2026
Port the full @chainsafe/swap-or-not-shuffle surface into the module
(exact validation order and errors, ComputeShuffledIndex, proposer and
sync committee index functions) and expose it as bindings.swapOrNotShuffle
with reference-identical names, semantics, and error messages.

Differential tests compare every function against the npm package;
2M-index sweeps show zero divergence. Perf benches show equal or faster
results than the Rust binding. Needed for ChainSafe/lodestar#9263.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

3 participants