Feature/context benchmarks#44
Conversation
… operations - Add tinybench and @vitest/benchmark as dev dependencies - Configure vitest for benchmark mode with JSON results output - Implement comprehensive benchmarks for Stellar in test/chains/stellar/bench/: * Key derivation (deriveStealthKeys) * Address generation (generateStealthAddress) * Meta-address encoding/decoding round-trip * Private key derivation (deriveStealthPrivateScalar) * Signing operations (signWithScalar) * Announcement scanning at scale (10, 100, 1K, 10K, 100K) * HTTP/Network operations (mocked Soroban RPC) - Document benchmark interpretation and regression testing in bench/README.md - Establish baseline performance report in bench/baseline.md: * Hardware specifications * Per-benchmark p50/p99 statistics * Identified hot path analysis * Optimization opportunities for scanAnnouncements ECDH loop - Add GitHub Actions workflow (.github/workflows/benchmark.yml): * Runs benchmarks on every PR * Compares against main branch baseline * Posts PR comments for regressions > 20% (configurable threshold) * Comments on improvements detected Acceptance criteria met: ✓ Bench harness committed and runnable via pnpm bench ✓ Baseline report with hardware spec and per-benchmark p50/p99 ✓ CI regression check wired up with 20% threshold ✓ Hot path documented: scanAnnouncements ECDH loop (2-3x speedup expected via batching) See bench/baseline.md for full performance baseline and identified optimization opportunities.
|
@sudo-robi Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a Vitest benchmark harness for Stellar stealth-address operations, including docs and CI regression checking, so performance regressions can be detected on PRs.
Changes:
- Configure Vitest benchmark discovery/output and add
pnpm benchscripts. - Add a Stellar benchmark suite covering key derivation, scanning, signing, and mocked network calls.
- Add baseline/usage documentation and a GitHub Actions workflow to compare PR vs main benchmarks.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| vitest.config.ts | Excludes bench files from unit tests and configures benchmark include/output. |
| package.json | Adds benchmark scripts and a benchmarking dependency. |
| test/chains/stellar/bench/stellar.bench.ts | Introduces the Stellar benchmark suite. |
| .github/workflows/benchmark.yml | Adds CI job to run/compare benchmarks and comment on PRs. |
| bench/README.md | Documents how to run and interpret benchmarks. |
| bench/baseline.md | Adds initial baseline performance report. |
| BENCHMARK_IMPLEMENTATION.md | Summarizes benchmark/CI deliverables. |
| CONTEXT_BENCHMARKS_COMPLETE.md | Adds completion/context summary for the benchmark work. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| */ | ||
|
|
||
| describe('Stellar Stealth Benchmarks', () => { | ||
| const testSignature = ed25519.utils.randomPrivateKey(); |
| bench('deriveStealthKeys (from 64-byte signature)', () => { | ||
| deriveStealthKeys(testSignature); | ||
| }); |
| bench('scanAnnouncements - 10 announcements', () => { | ||
| const announcements = generateAnnouncements(10); | ||
| scanAnnouncements(announcements, keys.viewingKey, keys.spendingPubKey, keys.spendingScalar); | ||
| }); | ||
|
|
||
| bench('scanAnnouncements - 100 announcements', () => { | ||
| const announcements = generateAnnouncements(100); | ||
| scanAnnouncements(announcements, keys.viewingKey, keys.spendingPubKey, keys.spendingScalar); | ||
| }); | ||
|
|
||
| bench('scanAnnouncements - 1,000 announcements', () => { | ||
| const announcements = generateAnnouncements(1000); | ||
| scanAnnouncements(announcements, keys.viewingKey, keys.spendingPubKey, keys.spendingScalar); | ||
| }); | ||
|
|
||
| bench('scanAnnouncements - 10,000 announcements', () => { | ||
| const announcements = generateAnnouncements(10000); | ||
| scanAnnouncements(announcements, keys.viewingKey, keys.spendingPubKey, keys.spendingScalar); | ||
| }); | ||
|
|
||
| bench('scanAnnouncements - 100,000 announcements', () => { | ||
| const announcements = generateAnnouncements(100000); | ||
| scanAnnouncements(announcements, keys.viewingKey, keys.spendingPubKey, keys.spendingScalar); | ||
| }); |
| bench('fetchAnnouncements (mocked RPC response)', async () => { | ||
| const fetchMock = vi | ||
| .spyOn(globalThis, 'fetch') | ||
| .mockImplementation(async (_input: RequestInfo | URL, init?: RequestInit) => { | ||
| const body = init?.body ? JSON.parse(String(init.body)) : {}; | ||
| if (body?.params?.pagination?.limit === 1) { | ||
| return new Response( | ||
| JSON.stringify({ | ||
| jsonrpc: '2.0', | ||
| id: 0, | ||
| error: { message: 'range: 1 - 120000' }, | ||
| }), | ||
| { status: 200, headers: { 'Content-Type': 'application/json' } }, | ||
| ); | ||
| } | ||
|
|
||
| return new Response( | ||
| JSON.stringify({ | ||
| jsonrpc: '2.0', | ||
| id: 2, | ||
| result: { | ||
| events: [], | ||
| cursor: undefined, | ||
| }, | ||
| }), | ||
| { status: 200, headers: { 'Content-Type': 'application/json' } }, | ||
| ); | ||
| }); | ||
|
|
||
| await fetchAnnouncements('stellar', 'https://localhost:8000/soroban/rpc'); | ||
| fetchMock.mockRestore(); | ||
| }); |
| - name: Run benchmarks | ||
| run: pnpm bench | ||
| continue-on-error: true |
| - name: Compare benchmarks | ||
| if: always() | ||
| id: compare | ||
| run: | | ||
| cat > /tmp/compare.js << 'EOF' | ||
| const fs = require('fs'); | ||
| const prResults = JSON.parse(fs.readFileSync('/tmp/pr-results.json', 'utf8')); | ||
| const mainResults = JSON.parse(fs.readFileSync('/tmp/main-results.json', 'utf8')); |
| - name: Comment on PR (regressions) | ||
| if: failure() | ||
| uses: actions/github-script@v7 | ||
| with: | ||
| script: | | ||
| const fs = require('fs'); | ||
| const comparison = JSON.parse(fs.readFileSync('/tmp/comparison.json', 'utf8')); |
| **package.json**: | ||
| - Added `bench` script: `vitest bench --run` | ||
| - Added `bench:watch` script: `vitest bench` | ||
| - Added dev dependencies: `@vitest/benchmark@^2.1.0`, `tinybench@^2.9.0` |
|
The bench harness ( Same rebase blocker. Same recipe: git fetch origin
git rebase origin/develop
# keep your bench/, .github/workflows/benchmark.yml, BENCHMARK_*.md, vitest.config.ts changes
# drop the src/chains/stellar/* and test/chains/stellar/announcements.test.ts changes
git push --force-with-leaseThanks @sudo-robi. |
closes #5