Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions .github/ISSUE_SPECS/006-measured-wasm-simd-decode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Measure and qualify a small Wasm SIMD decoder improvement

GitHub: https://github.com/misofm/codec/issues/6

## Objective and immutable scope

Enable standard Wasm SIMD in the existing FLAC decoder only if comparable repeated measurements show a useful improvement. Preserve exact canonical PCM, libFLAC integrity validation, bounded memory/foreign calls, cancellation, backpressure, public APIs, and existing supported-runtime qualification. No libFLAC rewrite, storage/delivery policy, WebGPU, format migration, or engine work.

Published baseline: codec 0.1.0 at `12f54c988a2df319064e6ca3885268d976319cc1`; decoder SHA-256 `5e282f9874ecb3f49b8ee8437efc318ec14ef5cff5b7580da9d875f94e5c5925`. Candidate base is origin/main `970119ce723666267aa28856f315d7b702945a93`, retaining source-equivalent release/docs fixes. Candidate tree `/tmp/miso-simd-study/candidates/codec`, branch `perf/simd-codec-6`. Read-only baseline tree `/tmp/miso-simd-study/worktrees/codec` must remain unchanged.

User-selected sequence supersedes local launch defaults: Sol high baseline → Astra xhigh scoped plan → Luna xhigh implementation → fresh Astra medium independent verification → identical after benchmarks → root release and app integration only if positive. Current status: baseline and scope complete; implementation, independent verification, positive comparison, and release pending. User authorizes the conditional sequence; no new routine approval step is required.

## Evidence informing the slice

Study root `/tmp/miso-simd-study`. Representative Bun 1.4.2 seven-round median: decode 57.345 ms (55.566–58.863) for 5,124,876 compressed bytes / 7,938,000 PCM bytes; adapter JS SHA 395.068 ms. Chromium 153.0.8010.12 whole Ghost r1 corpus: eight stems / 35 chunks / 92,685,354 compressed bytes / 249,323,400 active PCM bytes. Five-round one-worker decoder median 2,258.0 ms (2,248.4–2,330.8); eight-worker makespan 391.9 ms (390.7–394.8). All eight package, 35 compressed chunk, 35 decoded PCM chunk, and eight canonical hashes pass. Evidence files: `evidence/representative-bun-baseline.json`, `evidence/browser-baseline.json`.

These are isolated compute measurements, not HTTP/OPFS installation or app-open timings. The adapter's larger PCM verification cost is addressed separately in adapter #82, without changing delivery/storage or removing checks.

## Smallest implementation

1. Add `-msimd128` to actual decoder/libFLAC/wrapper/runtime compilation in `scripts/build-decoder-wasm.ts`, enable the necessary Binaryen SIMD feature through the existing Asyncify pipeline, and record flags/features in the reproducibility manifest. Keep Emscripten 6.0.9 and libFLAC 1.5.0 pins, `-O3`, ABI/imports, fixed 2 MiB memory, stack bounds, 256 KiB asset/input caps, and 384 KiB PCM output cap.
2. Update `wasm/flac-decoder.wasm`, its manifest, and `src/decoder-wasm.ts` public hash. Inspect actual Wasm disassembly for SIMD instructions and record the finding. Flags alone do not prove acceleration.
3. Compare the compiler-only candidate using the frozen benchmark workload before any manual SIMD work. If neutral/negative, retain published scalar and record the rejected experiment. Do not start a predictor rewrite or broad libFLAC fork. Return any proposed follow-on source adjustment to root with measured justification.
4. For a positive candidate, retain the existing asset URL/public API and document standard Wasm SIMD requirements. All currently advertised Bun/Node/browser worker gates must pass. If a supported runtime fails, keep scalar for this release rather than silently dropping support or building an unplanned dual-asset framework.

Owned files: decoder build script, decoder Wasm/manifest/public hash, focused decoder test adjustments, README, numbered spec/evidence, and eventual version/lock changes. Do not change encoder implementation, decoder Effect/control APIs, or original research worktrees. If Effect implementation changes become necessary, read and pin the user-requested Effect guidance first.

## Correctness and performance acceptance

Existing tests must retain exact mono/stereo16/24 PCM, variable-block behavior, CRC/MD5/truncation/count/metadata rejection, allocator/ABI cleanup, bounded inputs/outputs, downstream backpressure, and cancellation. Rebuild reproducibly and verify fixed memory/imports/exports/asset size. Add only a missing discriminating SIMD-boundary test if existing tests do not cover it.

Rerun `harness/representative.mjs` and `harness/run-browser.mjs` with the same source fragmentation, corpus, CPU/runtime/browser, warmups, seven Bun rounds and five browser decode rounds. Record actual candidate source/tree/version and Wasm hash; source labels must be parameterized without changing timed work. Run CPU-heavy work serially with benchmark ownership. Accept a repeatable whole-corpus improvement larger than observed variation, without a material regression in single/eight-worker modes or supported runtimes. Keep raw samples, medians/ranges, percent differences, initialization/compile cost, asset size, and bounds. A single fast sample is insufficient; do not retry repeatedly looking for a win.

## Required qualification and release

Run `bun run check`, pinned-native `bun run test:integration`, `bun run build:wasm:verify`, `bun run build`, and `bun run pack:check`. Keep the existing isolated packed consumer matrix: Bun 1.4.2, Node 22.23.2, Effect rc.112/rc.115, Chromium/Firefox/WebKit workers. Preserve source/toolchain/license provenance and both Wasm reproducibility gates. Do not commit corpus media, caches, dependencies, secrets, or tarballs.

Fresh Astra medium independently verifies a frozen candidate and records full commit/tree, files, commands/results, packed archive provenance, benchmark comparison, verdict, and remaining blockers here. Root owns versioning, merge, trusted publication using `.github/RELEASING.md`, registry artifact verification, and downstream adapter/app installation. Codec may remain unchanged if its candidate is not positive; that does not prevent a qualified adapter-only improvement. No implementation or publication is claimed by this scope.

Companion plan: `/tmp/miso-simd-study/simd-plan.md`; adapter issue https://github.com/misofm/engine-web-adapter/issues/82. Technical reference: https://emscripten.org/docs/porting/simd.html .

## Luna implementation evidence (candidate, not published)

The compiler-only candidate was built with the pinned Emscripten 6.0.9
checkout and libFLAC 1.5.0 archive. `-msimd128` is present in configure
`CFLAGS` and every decoder, libFLAC, wrapper, and runtime compile. The existing
Asyncify pipeline now passes `--enable-simd`; the manifest records the exact
compiler flag and `bulk-memory`/`simd128` features. The rebuilt fixed-memory
asset is 75,923 bytes with SHA-256
`70caf38185675dff89498e89f98171d49ec6f143a56c6895088d93c35e2018cd`.

`wasm2wat wasm/flac-decoder.wasm` contains 62 `v128.const`, 23 `v128.load`,
80 `v128.store`, 20 `i8x16.shuffle`, and additional typed SIMD operations.
This is actual SIMD in the asset; the compiler flag alone was not used as
evidence. No libFLAC source or decoder control-flow changes were made.

On the frozen representative Bun workload (Bun 1.4.2, seven rounds), decode
was 54.872 ms (53.596–71.818) versus the scalar baseline 57.345 ms
(55.566–58.863), a 4.31% median reduction. On Chromium 153.0.8010.12 and the
full eight-stem/35-chunk corpus, one-worker decode medians were 2,179.1 ms
(2,140.4–2,191.7) and 2,155.5 ms (2,150.3–2,192.5) in two candidate runs;
the published baseline was 2,258.0 ms (2,248.4–2,330.8), with an adjacent
unchanged baseline at 2,243.9 ms (2,229.0–2,298.4). Eight-worker makespan
medians were 378.1 ms (370.2–390.4) and 377.7 ms (372.5–379.8), versus the
published 391.9 ms (390.7–394.8) and adjacent unchanged 394.1 ms
(388.8–422.4). All package, compressed chunk, decoded PCM chunk, canonical
PCM, libFLAC MD5, and count checks passed in each harness run.

Local gates passed: `bun run check` (40 passed, 5 skipped native-only),
`CODEC_NATIVE_FLAC=/data/codec-tooling/flac-scalar/src/flac/flac bun run
test:integration` (5 passed), `bun run build:wasm:verify`, `bun run build`, and
`bun run pack:check` (Bun/Node and Chromium/Firefox/WebKit packed consumers for
Effect rc.112 and rc.115). Fresh independent review and root release remain
pending; this candidate does not claim publication.

The frozen implementation commit is `6265973b730230009b6d07705f1641a948120105`.
The post-commit final browser record is
`/tmp/miso-simd-study/evidence/browser-candidate-final.json`; it reports this
codec commit and decoder hash, with one-worker decode 2,200.9 ms
(2,181.7–2,239.6), eight-worker makespan 386.2 ms (371.3–397.9), and all
547,303,704 bytes of indexed PCM verification passing.

## Independent verification and official acceptance

Fresh Astra medium verified the frozen runtime and the adapter CI correction; no implementation blockers remain. Sol high repeated the frozen corpus after review: Chromium serial decode 2232.9 → 2175.5 ms, eight-worker decode 399.9 → 382.7 ms, indexed PCM verification 11568.0 → 2193.2 ms. Every digest and count passed. Both candidates are accepted for release. Most hashing improvement comes from scalar Wasm; the separately measured SIMD increment is 1.54%. These are compute measurements, not app-open timings.

Full review, report, and immutable raw measurements are retained in `.github/RELEASE_EVIDENCE/006/`. Root is preparing the release; publication remains pending.
135 changes: 135 additions & 0 deletions .github/RELEASE_EVIDENCE/006/AFTER.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
# Official post-verification comparison

Measured 2026-09-13 after the fresh Astra medium review passed and its sole CI
blocker was repaired/rechecked. The compared candidate trees stayed clean and
frozen throughout the run:

- Codec `8c2823b041594a38284b01b5a39d77a6b515716e`, tree
`0028688d80b1baf794508340bc477da590be4b38`, decoder asset SHA-256
`70caf38185675dff89498e89f98171d49ec6f143a56c6895088d93c35e2018cd`.
- Adapter `088a14f57a945e6feb93145f3338943276a335dc`, tree
`d453f0937c99954bd7a8917cec06ce2682f77ee5`, generated selected SHA module
payload SHA-256
`4435c2078c2783a77c06532e480d195ce314f500b849688d89d494432789fc28`.

The adjacent unchanged controls are published codec `0.1.0`
`12f54c988a2df319064e6ca3885268d976319cc1` and adapter `0.4.0`
`9b8172fd52746907c113442995dc7538cfe5ad13`. Both runs used the same Linux
x86_64 AMD EPYC 7313P host, Node 22.23.2 runner, Playwright Core 1.62.1, Vite
8.2.2, and headless Chromium 153.0.8010.12 binary
`8c599d43aec53f2460a31ae2f4af6bd863f8258b34ff519564bc5d4726bfaa1e`.
System load after the serial benchmark sequence was 0.76/1.06/1.10. No
competing builds or study benchmarks ran in the window.

## Correctness and workload identity

The official baseline and candidate each admitted the same eight published
Ghost `r1` `MISOSTM1` packages and passed all 8 package SHA-256, 35 compressed
FLAC SHA-256, 35 decoded chunk PCM SHA-256, and 8 reconstructed canonical PCM
SHA-256 checks. The corpus remained 92,722,644 container bytes, 92,685,354
compressed payload bytes, 249,323,400 decoded active PCM bytes, 48,656,904
synthesized zero bytes, and 297,980,304 canonical PCM bytes. The indexed PCM
verification workload remained 547,303,704 hashed bytes. Maximum observed PCM
output remained 27,648 bytes. The raw JSON records each source identity and
package digest; the representative chunk independently matched compressed
SHA-256 `ab35d73ddbfb7cbeb9384a34c585edc0141e88bb812d047caad4969a4abe884b`
and PCM SHA-256
`649b462aa016a111211498488ffca8da24e16f1a9b594f58bf38fb03933bb4f5`
with both adapter and Node crypto, and libFLAC MD5 was checked and verified.

## Official adjacent Chromium results

Each decoder case had one untimed warm-up and five measured rounds. Main-thread
PCM verification had one warm-up and three measured rounds. Local HTTP reads,
package/compressed correctness hashes, manifest admission, Wasm fetch/compile,
worker construction, network/CDN, OPFS, quota estimation, structured-clone
acknowledgements, and input/output waits are outside the timed sections.

| Slice | Published adjacent samples (ms) | Candidate samples (ms) | Median change | Candidate CV / throughput |
| ---------------------------- | -------------------------------------- | -------------------------------------- | ----------------------------------: | ------------------------: |
| One-worker FLAC decode | 2197.2, 2232.9, 2268.7, 2243.6, 2226.3 | 2155.4, 2175.5, 2187.5, 2167.5, 2199.7 | 2232.9 → 2175.5, **2.57% faster** | 0.71%; 109.30 MiB/s |
| Eight-worker decode makespan | 399.9, 404.9, 399.9, 391.7, 416.3 | 371.3, 404.3, 379.3, 382.7, 394.6 | 399.9 → 382.7, **4.30% faster** | 3.02%; 621.30 MiB/s |
| Main-thread indexed PCM SHA | 11589.8, 11568.0, 11550.9 | 2184.3, 2201.6, 2193.2 | 11568.0 → 2193.2, **81.04% faster** | 0.32%; 237.99 MiB/s |

The unchanged-control CVs were 1.04%, 2.01%, and 0.14% respectively. Decoder
gains are modest. The one-worker median improvement exceeds run variation and
the result agrees with the earlier independent candidate runs. The eight-worker
ranges overlap under greater scheduling noise, but its 4.30% median gain and
the earlier adjacent 4.16% result have the same direction and magnitude. The
compiler-only decoder candidate therefore passes this host's compute gate in
both serial and default-eight-worker modes.

The SHA result is decisive and does not overlap the control. For attribution,
the same-host development control measured scalar Wasm at a 2224.7 ms median
against its adjacent unchanged-JS median of 11540.5 ms: **scalar Wasm reduces
time by 80.72% versus JS**. Selected SIMD measured
2190.4 ms in the paired control, another **1.54% faster than scalar Wasm**; its
adjacent repeat was 2189.8 ms and the official frozen result is 2193.2 ms. SIMD
adds a small repeatable gain to the much larger Wasm gain.

## Representative Bun results

The identical largest indexed chunk was rerun for seven rounds on Bun 1.4.2.
Bun's reported `process.version` is compatibility metadata and is not a Node 26
qualification.

| Slice | Published adjacent median (range; CV) | Candidate median (range; CV) | Change |
| -------------------------------- | ------------------------------------: | -------------------------------: | ----------------: |
| FLAC decode, 7,938,000 PCM bytes | 60.117 ms (57.117–63.479; 3.85%) | 55.460 ms (51.870–62.195; 5.68%) | **7.75% faster** |
| PCM SHA-256, 7,938,000 bytes | 389.382 ms (385.286–391.515; 0.55%) | 33.247 ms (33.181–33.836; 0.66%) | **91.46% faster** |

Exact samples are retained in the official Bun JSON files. The representative
result supports the browser direction; the whole-corpus Chromium comparison is
the primary gate.

## Assets, initialization, and memory

The decoder asset grew from 74,338 to 75,923 bytes: +1,585 bytes (+2.13%). Its
fixed 2 MiB memory, ABI, imports/exports, 256 KiB input cap, and 384 KiB maximum
output cap are unchanged. Browser decoder compile/validation was 2.0 ms for the
adjacent control and 3.3 ms for the candidate; Bun measured 4.676 and 4.332 ms.
These are single setup observations and are excluded from throughput.

The selected SHA compression module is 957 bytes, versus 1,347 bytes for the
scalar Wasm control. It has no imports, one fixed 64 KiB memory, a 16 KiB input
scratch/batch cap, and no growth. A single Bun observation of the candidate's
first 16 KiB hash was 1.094 ms versus 0.280 ms warm, placing a coarse upper bound
of about 0.814 ms on lazy compile/instance overhead on this host. The operation
also includes hashing and JIT effects, so it is not a pure compile measurement.

Adapter fixed-buffer accounting rises by 131,072 bytes, from 4,853,776 to
4,984,848 bytes, leaving 3,403,760 bytes within the unchanged 8 MiB worker
reservation. Per-hash JS state/tail/length ownership and API semantics remain
unchanged. Independent verification confirmed fixed memories, failed growth,
bounded copies, digest equivalence, packed deployment, and full test gates.

## Recommendation and limits

**Codec decoder: ship the reviewed SIMD candidate.** It gives a repeatable but
modest 2.57% one-worker and 4.30% eight-worker Chromium improvement with exact
PCM and unchanged bounds. The small asset/compile changes are acceptable in the
measured scope.

**Adapter SHA: ship the reviewed selected SIMD candidate.** The production
selection reduces current indexed main-thread PCM verification compute by
81.04%; scalar Wasm separately reduces time by 80.72% versus JS, and SIMD adds a
measured 1.54% reduction versus scalar Wasm. Correctness and bounded-memory
gates remain intact.

These are isolated cached-compute findings. They make no app-open, CDN/network,
OPFS, mobile, or end-to-end latency claim. App integration and release remain
root-owned follow-up work.

Official raw evidence:

- `browser-official-adjacent-baseline.json` SHA-256
`514844c7c52396fa94f861f0a9b4ca27fc0feba5bb1d7ca7c477dd1dfa6bc9df`
- `browser-official-candidate.json` SHA-256
`44d1c75c80963317bbe8033f6732ac0770e274fcbf60e23e1e48c3dc09731dbf`
- `representative-bun-official-adjacent-baseline.json` SHA-256
`8070ac00dc4bfdbcb756c033c3c634a4ef8f20e927bf5ae1b4c645829a5f3809`
- `representative-bun-official-candidate.json` SHA-256
`e094254f2838e75ff4769f9c6d63b54f9a6b76eac1fc4ae7dd14c6993befe272`

Exact rerun commands and the parameterized source-root contract remain in
`../harness/README.md`.
Loading
Loading