Skip to content

feat(perf): automated Pluto-vs-Charon benchmark harness + first optimization pass#551

Open
varex83 wants to merge 3 commits into
mainfrom
perf/vs-charon-harness
Open

feat(perf): automated Pluto-vs-Charon benchmark harness + first optimization pass#551
varex83 wants to merge 3 commits into
mainfrom
perf/vs-charon-harness

Conversation

@varex83

@varex83 varex83 commented Jul 15, 2026

Copy link
Copy Markdown
Collaborator

Summary

Two commits: a fully automated performance-comparison harness against Charon, and the first optimization pass driven by its findings.

1. Performance harness (perf/)

One command produces a side-by-side report flagging every workload where Pluto is slower than Charon (ratio > 1.15SUBOPTIMAL):

./perf/run.sh --tier 12     # micro + in-memory component benches
./perf/run.sh --tier 3      # process-level (CLI wall time, RSS, DKG ceremony)
  • Tier 1 (pure compute): criterion benches for BLS tbls (blst vs herumi), FROST DKG rounds, SSZ codecs, protobuf, secp256k1. Go counterparts live in a separate perf/go-bench module consuming the vendored charon source via replace directives — charon/ stays pristine.
  • Tier 2 (in-memory): full QBFT consensus instance (spawn→all-decided) with mirrored topologies on both sides; in-memory FROST DKG ceremony via a new default-off bench-util feature.
  • Tier 3 (process level): hyperfine matrix for create enr/create cluster, peak RSS, timed all-pluto vs all-charon DKG ceremonies through scripts/dkg-runner.
  • Workloads are byte-identical across languages: shared binary fixtures in perf/fixtures/, and each Rust bench asserts a round-trip against the fixture before timing.
  • perf.yml workflow: nightly + on-demand tiers 1+2, report in the job summary, baseline regression gating (exit 2) once a blessed perf/baseline.json is committed.
  • Initial findings report: perf/FINDINGS.md (5 root causes, prioritized by duty-hot-path impact).

2. Optimization pass (FINDINGS items 1, 2, 4)

pair before after charon
tbls/threshold_aggregate 3-of-4 407µs 205µs 300µs
tbls/threshold_aggregate 7-of-10 951µs 404µs 792µs
frost/aggregate 346µs 148µs 300µs
tbls/threshold_split 7-of-10 15.1µs 10.0µs 5.0µs
tbls/recover_secret 7-of-10 14.5µs 10.1µs 4.0µs
  • blst_impl / pluto-frost: Lagrange signature interpolation now uses blst Pippenger multi-scalar multiplication with a single final affine conversion (was one scalar mult + one field-inversion-costing affine conversion per share); polynomial evaluation and secret interpolation moved to fr-domain arithmetic with volatile wiping of secret intermediates. All five previously SUBOPTIMAL aggregation pairs (per-duty sigagg path) now beat charon.
  • ssz_codec: exact buffer preallocation, payload appended directly after versioned headers (no temp-Vec copy), and the unsigned Deneb/Electra/Fulu proposal path no longer deep-clones the block, KZG proofs and blobs to serialize.
  • Release profile: thin LTO + codegen-units=1 (~6–12% measured on pure-Rust paths).

Deferred per FINDINGS: QBFT instance lifecycle (needs a design pass) and ceremony-path items (scrypt, k1 sign). Remaining split/recover gap (~2x) is checked per-share validation + HKDF-based coefficient keygen, kept deliberately.

Testing

  • Full workspace suite green: 2029 tests (only failures are Docker-gated eth2api integration tests — no Docker on the bench machine, unrelated).
  • cargo clippy --workspace --all-targets --all-features -- -D warnings, cargo +nightly fmt --all --check clean.
  • Frost kryptology-interop fixture tests pass (wire-format parity preserved); error semantics preserved throughout the crypto rewrites.
  • Determinism: tier-1 ratios stable across repeated runs; SSZ/proto fixture round-trip assertions confirm byte-identical workloads.

🤖 Generated with Claude Code

varex83 added 2 commits July 15, 2026 20:41
One command (perf/run.sh) runs matching workloads on both implementations,
normalizes the results, and renders a report flagging every pair where Pluto
is slower than Charon (ratio > 1.15 -> SUBOPTIMAL).

- Tier 1 (pure compute): criterion benches for BLS tbls, FROST DKG rounds,
  SSZ codecs, protobuf, plus canonical ids for the existing k1util bench;
  Go counterparts live in a separate perf/go-bench module that consumes the
  vendored charon source via replace directives (charon/ stays pristine).
- Tier 2 (in-memory): full QBFT consensus instance benches on both sides
  with mirrored topologies; in-memory FROST DKG ceremony (Rust-only) via a
  new bench-util feature exposing the test harness in pluto-dkg.
- Tier 3 (process level): hyperfine matrix for create enr/cluster, peak RSS
  capture, and timed all-pluto vs all-charon DKG ceremonies through
  scripts/dkg-runner.
- Shared binary fixtures in perf/fixtures generated by the Go side; the
  Rust benches assert a byte-identical round-trip before timing.
- perf.yml workflow: nightly + on-demand tiers 1+2, report in the job
  summary, artifact upload, baseline regression gating (exit 2) once
  perf/baseline.json is committed.
First optimization pass over the harness findings (perf/FINDINGS.md):

- blst_impl: Lagrange signature interpolation now uses blst Pippenger
  multi-scalar multiplication with a single final affine conversion instead
  of one scalar mult plus one field-inversion-costing affine conversion per
  share. Polynomial evaluation (threshold_split) and secret interpolation
  (recover_secret) moved to fr-domain Horner/dot-product arithmetic; fr
  copies of secret material are volatile-wiped (SecretFrVec drop guard).
- pluto-frost: same Pippenger treatment in
  BlsSignature::from_partial_signatures, with batch affine conversion.
- ssz_codec: all encoders pre-size output buffers via ssz_bytes_len;
  versioned encoders append the payload directly after the header instead
  of encoding into a temporary Vec and copying; the unsigned
  Deneb/Electra/Fulu proposal path no longer deep-clones the block, KZG
  proofs and blobs to serialize (borrowing *BlockContentsRef structs).
- Release profile: thin LTO + codegen-units=1 (~6-12% on pure-Rust paths,
  measured; no effect on blst-bound ones).

Measured (Apple M3 Pro, vs charon v1.7.1 herumi):
- tbls/threshold_aggregate 3-of-4: 407us -> 205us (charon 300us)
- tbls/threshold_aggregate 7-of-10: 951us -> 404us (charon 792us)
- frost/aggregate: 346us -> 148us (charon 300us)
- tbls/threshold_split 7-of-10: 15.1us -> 10.0us
- tbls/recover_secret 7-of-10: 14.5us -> 10.1us

All five previously SUBOPTIMAL aggregation pairs now beat charon; split/
recover remain ~2x (checked per-share validation and HKDF-based coefficient
keygen, kept deliberately). Error semantics preserved throughout.
Comment thread .github/workflows/perf.yml Fixed
Comment thread .github/workflows/perf.yml Fixed
@varex83agent varex83agent changed the title perf: automated Pluto-vs-Charon benchmark harness + first optimization pass feat(perf): automated Pluto-vs-Charon benchmark harness + first optimization pass Jul 17, 2026
Pin dtolnay/rust-toolchain and Swatinem/rust-cache to immutable commit
SHAs to resolve CodeQL unpinned-action alerts.

Co-authored-by: Bohdan Ohorodnii <35969035+varex83@users.noreply.github.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.

3 participants