Skip to content

ci(bench): continuous wasm dashboard shard vs @bokuweb/zstd-wasm - #372

Merged
polaz merged 2 commits into
mainfrom
feat/#366-cibench-continuous-wasm-dashboard-shard-structured
Jun 7, 2026
Merged

polaz merged 2 commits into
mainfrom
feat/#366-cibench-continuous-wasm-dashboard-shard-structured

Conversation

@polaz

@polaz polaz commented Jun 7, 2026

Copy link
Copy Markdown
Member

Summary

Tracks the WebAssembly build (simd128 + scalar) against the most popular npm wasm zstd, @bokuweb/zstd-wasm, on the gh-pages perf dashboard over time. Previously the wasm-vs-competitor comparison ran only locally as a pre-publish gate, so a wasm perf/ratio regression would only surface if someone re-ran the bench by hand.

What changed

  • CI shard bench-wasm (ci.yml): push-to-main only, gated on a new wasm_core path filter (zstd/**, zstd-wasm/**, Cargo.*, toolchain). Builds the npm payloads, runs node zstd-wasm/bench/bench.mjs, captures its REPORT* lines, and publishes a dedicated wasm timeseries to gh-pages. Independent of the rust_core-gated native matrix so a wasm-only change still updates the dashboard. Publishes only benchmark-wasm.json (index.html stays owned by benchmark-pages / pages-only), so the two publishers write disjoint files and never race; a rebase-retry loop guards the rare concurrent-push case.
  • Aggregation scripts: parse-wasm-bench.py turns the REPORT / REPORT_DICT engine triplet (ours-simd128, ours-scalar, bokuweb) into records carrying ratio + compress/decompress throughput; merge-wasm-bench.py accumulates them into the persisted benchmark-wasm.json with 180-day retention and per-snapshot dedup.
  • Dashboard (index.html): a dedicated WebAssembly section plots compress speed, decompress speed, and output ratio per engine over time, with our throughput shown as a multiple of bokuweb (>1 = faster). Degrades gracefully when no wasm data is published yet — the rest of the dashboard is unaffected.
  • Docs: BENCHMARKS.md (new section) + README.md.

Acceptance criteria

  • Push-to-main runs the wasm bench shard; REPORT* lines captured + aggregated.
  • dev/bench dashboard shows wasm simd128/scalar vs bokuweb ratio + speed over time.
  • No PR-time cost (shard is push-only).

Testing

All verified against real output, not synthetic:

  • Real payload build + node bench.mjs (exit 0, all roundtrips ok).
  • Parser on real output → 84 records (72 plain + 12 dict = 6×4×3 + 2×2×3).
  • Merge: accumulation (6→12), dedup of a re-run snapshot (stays 12), retention.
  • Dashboard in a node mock-DOM harness on real data: 3 engine lines + 2 ÷bokuweb ratio lines; switching to the ratio metric drops the secondary axis; values correct.
  • 404 path (no data yet): empty-state message, no chart created, main dashboard unaffected.
  • node --check (JS), py_compile (scripts), YAML validity (ci.yml).

Note on the issue plan

The issue suggested extending aggregate-bench-levels.py / merge-benchmarks.py, but those run inside rust_core-gated jobs — routing wasm through them would break the "push-to-main runs the wasm shard" criterion (a wasm-only change wouldn't update the dashboard). Implemented as a self-contained wasm pipeline instead; the goal (continuous wasm tracking) is fully met.

Closes #366

Summary by CodeRabbit

  • New Features

    • Added a dedicated WebAssembly performance dashboard with time-series comparisons across simd128, scalar, and a reference npm implementation.
    • Dashboard UI now includes metric/scenario/level selectors, per-engine lines and ratio overlays, live legend, and status messaging for empty or missing data.
    • CI now runs WebAssembly benchmarks and automatically updates the dashboard data on changes.
  • Documentation

    • Updated performance docs and README to cover WebAssembly benchmarking and dashboard coverage.

Track the WebAssembly build (simd128 + scalar) against the most popular
npm wasm zstd, @bokuweb/zstd-wasm, on the gh-pages perf dashboard over
time — previously the wasm-vs-competitor comparison ran only locally as
a pre-publish gate.

- Add push-to-main `bench-wasm` CI shard: builds the npm payloads, runs
  `node zstd-wasm/bench/bench.mjs`, captures its REPORT* lines, and
  publishes a dedicated wasm timeseries to gh-pages. Gated on a new
  `wasm_core` path filter and independent of the rust_core-gated native
  matrix, so a wasm-only change still updates the dashboard. Publishes
  only benchmark-wasm.json (index.html stays owned by benchmark-pages /
  pages-only), so the two publishers write disjoint files.
- Add parse-wasm-bench.py (REPORT / REPORT_DICT engine triplet ->
  records with ratio + compress/decompress throughput) and
  merge-wasm-bench.py (accumulate into persisted benchmark-wasm.json,
  180-day retention, dedup per snapshot).
- Add a WebAssembly section to the dashboard plotting compress speed,
  decompress speed, and output ratio per engine over time, with our
  throughput shown as a multiple of bokuweb. Degrades gracefully when
  no wasm data is published yet.
- Document the wasm comparison in BENCHMARKS.md + README.md.

Closes #366
@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e48c6075-20ac-45a0-ba62-18bf9305f31a

📥 Commits

Reviewing files that changed from the base of the PR and between 5d42549 and 0ccda71.

📒 Files selected for processing (2)
  • .github/bench-dashboard/index.html
  • .github/scripts/merge-wasm-bench.py

📝 Walkthrough

Walkthrough

Adds a push-to-main wasm benchmarking shard plus parsing and merge scripts, publishes merged wasm timeseries to gh-pages, and renders an independent WebAssembly comparison card on the dashboard (simd128/scalar vs @bokuweb/zstd-wasm) with selectable metrics and ratio overlays.

Changes

Wasm Benchmarking Pipeline

Layer / File(s) Summary
Parse benchmark stdout to JSON
.github/scripts/parse-wasm-bench.py
Extracts REPORT* lines, parses key/value fields, computes throughput (bytes/sec), records roundtrip failures, enriches with commit metadata, and writes benchmark-wasm-run.json.
Merge and retain timeseries history
.github/scripts/merge-wasm-bench.py
Merges a run into benchmark-wasm.json with composite-key deduplication, sorts by generated_at, applies retention and max-records truncation, and writes the merged payload.
CI workflow orchestration
.github/workflows/ci.yml
Adds wasm_core change-detection output and a push-only bench-wasm job that builds npm wasm, runs the Node harness, parses the run, merges history, and publishes to gh-pages with retry/rebase on conflicts.
Dashboard wasm section UI and metrics
.github/bench-dashboard/index.html
Adds a WebAssembly dashboard card with metric/kind/scenario/level selectors, engine styles, and logical metrics (compress/decompress in MiB/s, output ratio).
Dashboard chart building and rendering
.github/bench-dashboard/index.html
Fetches benchmark-wasm.json, aggregates records into per-engine time-series by snapshot (per-cell mean), renders Chart.js with optional dashed secondary-axis ours ÷ bokuweb series for speed metrics, updates legend/status, and handles empty/404 states.
Documentation
BENCHMARKS.md, README.md
Documents the wasm benchmarking pipeline, CI shard, local run steps, and schema of benchmark-wasm.json; updates README performance note about the wasm dashboard.

Sequence Diagram(s)

sequenceDiagram
  participant CI as CI (bench-wasm)
  participant Parser as parse-wasm-bench.py
  participant Merger as merge-wasm-bench.py
  participant GHPages as gh-pages
  participant Dashboard as Dashboard (Browser)
  
  CI->>CI: Build npm wasm payloads
  CI->>CI: Run Node benchmark harness
  CI->>Parser: Parse REPORT* stdout
  Parser-->>CI: benchmark-wasm-run.json
  CI->>Merger: Load run + existing history
  Merger-->>CI: Deduplicate, sort, retain
  Merger-->>GHPages: benchmark-wasm.json (merged)
  Dashboard->>GHPages: Fetch benchmark-wasm.json
  GHPages-->>Dashboard: Timestamped engine records
  Dashboard->>Dashboard: Aggregate by snapshot
  Dashboard->>Dashboard: Build Chart.js datasets
  Dashboard->>Dashboard: Optional: ours÷bokuweb ratio
  Dashboard-->>Dashboard: Render chart + legend
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 I hopped through runs at break of dawn,

Collected reports till data's drawn,
Merged the timeseries, kept it neat,
Pushed to pages—charts look sweet,
Now wasm and bokuweb race on the lawn. 🥕📈

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding a continuous CI shard for wasm benchmark dashboard tracking against @bokuweb/zstd-wasm. It is clear, concise, and directly reflects the core objective.
Linked Issues check ✅ Passed All three acceptance criteria from issue #366 are met: CI shard captures REPORT lines on push-to-main [#366], dashboard renders wasm comparison over time [#366], and benchmark runs only on push-to-main avoiding PR-time cost [#366].
Out of Scope Changes check ✅ Passed All changes are directly aligned with #366 objectives: CI workflow updates, wasm-specific aggregation scripts, dashboard rendering, and documentation updates. No unrelated modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/#366-cibench-continuous-wasm-dashboard-shard-structured

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented Jun 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/bench-dashboard/index.html:
- Around line 2269-2275: The current fetch for "./benchmark-wasm.json" treats
any non-ok response as the empty-state; change the logic so only a 404 triggers
the empty-state path (call renderWasmChart() and bindWasmControls()); for other
non-2xx statuses surface the failure (e.g., throw or call
console.error/processLogger and render an error UI) so real publish/runtime
failures aren't masked. Update the fetch handling around the response.ok check
to explicitly check response.status === 404 and otherwise handle/report the
error, keeping renderWasmChart and bindWasmControls as the 404-only branch.
- Around line 367-368: The new canvas element with id `#chart-wasm` is not
included in the shared sizing CSS selector (`#chart`, `#chart-profile`,
`#chart-aggregate`), so it uses the browser default height; update that selector
to include `#chart-wasm` (i.e., change "`#chart`, `#chart-profile`, `#chart-aggregate`"
to include "`#chart-wasm`") so the `#chart-wasm` canvas inherits the same sizing
rules as `#chart`, `#chart-profile`, and `#chart-aggregate`.

In @.github/scripts/merge-wasm-bench.py:
- Around line 48-55: The load_records function currently calls
json.loads(p.read_text()) which will raise JSONDecodeError on corrupted JSON;
update load_records to catch json.JSONDecodeError (and optionally ValueError for
compatibility) around the json.loads call, log or warn that the existing
benchmark-wasm.json is corrupted, and return an empty list so merging continues
with fresh history; keep the existing early returns for missing path/file, and
only wrap the payload parsing (the json.loads and payload.get("records", []))
inside the try/except handling in load_records.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 840353a7-7d86-41c7-ac3c-c7dd760b0381

📥 Commits

Reviewing files that changed from the base of the PR and between 8ef222d and 5d42549.

📒 Files selected for processing (6)
  • .github/bench-dashboard/index.html
  • .github/scripts/merge-wasm-bench.py
  • .github/scripts/parse-wasm-bench.py
  • .github/workflows/ci.yml
  • BENCHMARKS.md
  • README.md

Comment thread .github/bench-dashboard/index.html
Comment thread .github/bench-dashboard/index.html
Comment thread .github/scripts/merge-wasm-bench.py
- index.html: size the new #chart-wasm canvas via the shared 440px rule
  (was falling back to the browser default canvas height).
- index.html: only treat a 404 from benchmark-wasm.json as the
  "no wasm data yet" empty state; surface any other non-2xx (500/403/
  broken publish) as an error instead of masking it.
- merge-wasm-bench.py: tolerate a corrupted persisted benchmark-wasm.json
  (warn + rebuild from this run's records) so one bad file can't wedge
  every future wasm publish.
@polaz
polaz merged commit 5aafb74 into main Jun 7, 2026
25 checks passed
@polaz
polaz deleted the feat/#366-cibench-continuous-wasm-dashboard-shard-structured branch June 7, 2026 20:11
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.

ci(bench): continuous wasm dashboard shard (structured-zstd wasm vs @bokuweb/zstd-wasm)

1 participant