Prefer WASM on firefox cause WebGPU is incredibly slow there.
This commit is contained in:
@@ -118,7 +118,13 @@ are the remaining M5 work; see `WorkBreakdown.md`.)
|
||||
`navigator.ml` (WebNN) first, then `navigator.gpu?.requestAdapter()` (WebGPU),
|
||||
and picks in priority order WebNN → WebGPU → WASM; if pipeline creation on the
|
||||
picked device throws, it retries down the same chain (WASM always last). An
|
||||
explicit `"webnn"`/`"webgpu"` preference falls back straight to WASM. It then
|
||||
explicit `"webnn"`/`"webgpu"` preference falls back straight to WASM.
|
||||
Two D-025 refinements: WebGPU is skipped up front when the dtype it would
|
||||
use needs fp16 but the adapter lacks `shader-f16` (Firefox), and a WASM
|
||||
fallback *after* a failed accelerator attempt does not run in-place — the
|
||||
failed attempt pinned ort's thread pool at 1 — but posts `load-restart`,
|
||||
and the UI respawns the worker with an explicit `wasm` load (transcribe
|
||||
posts wait on a ready gate in `App.tsx`, so none are lost). It then
|
||||
resolves the catalog's dtype default for the device actually used and creates
|
||||
the Transformers.js `automatic-speech-recognition` pipeline.
|
||||
Download progress events are forwarded to the UI; the terminal
|
||||
|
||||
@@ -437,3 +437,105 @@ start's region. Covered by `tests/live.test.ts` (batching + cap) and the WER
|
||||
gates (`tests/live-accuracy.test.ts`, `tests/live-real-model.test.ts` — the
|
||||
real-model test's fast feed makes real inference lag, so merging is exercised
|
||||
there with real weights).
|
||||
|
||||
## D-025 — A degraded-thread WASM fallback restarts the worker; WebGPU is skipped when fp16 needs an absent shader-f16
|
||||
**Status:** Accepted (2026-07-17)
|
||||
Reported symptom: Firefox performance far below Chrome, live transcription
|
||||
worst. Chrome resolves D-017's auto chain to an accelerator; Firefox has no
|
||||
WebNN and its WebGPU (where exposed at all) fails onnxruntime-web's
|
||||
Chromium-built JSEP EP — notably it lacks the `shader-f16` feature, which the
|
||||
catalog's fp16-encoder WebGPU defaults (small, large-v3-turbo) need. So
|
||||
Firefox lands on WASM — but *how* it lands there was the real bug: the failed
|
||||
WebGPU attempt initializes ort's WASM runtime at the accelerator-safe
|
||||
`numThreads: 1` (D-023 amendment), the thread pool is fixed at first init,
|
||||
and the fallback session then runs **single-threaded for the worker's whole
|
||||
life** (measured 5.5×: 52 s vs 9.5 s on a 14 s clip). Live never respawns the
|
||||
worker, and every live pass pads to a full 30 s window (D-024), so passes
|
||||
took ~40–60 s: tentative text froze and the stop drain ballooned — D-023
|
||||
amendment's "correct over fast" edge case is Firefox's *default* path.
|
||||
**Fix (three parts):**
|
||||
1. **Restart instead of a degraded fallback.** When `createPipeline` reaches
|
||||
the `wasm` attempt after any accelerator attempt ran and multithreading is
|
||||
actually available (`desiredWasmThreads() > 1`), the worker posts
|
||||
`{type:"load-restart", modelId, preference, device:"wasm"}` and aborts the
|
||||
load (sentinel error, never surfaced). The UI terminates the worker,
|
||||
respawns it, re-issues the load with the explicit device, and remembers
|
||||
`modelId::preference → device` for the session so later loads skip the
|
||||
doomed attempt (session-only: a browser update may fix the backend; an
|
||||
explicit user backend choice is a different key, so it retries once).
|
||||
To make the respawn safe, transcribe posts now wait on a **load gate** in
|
||||
`App.tsx` that resolves on `ready` (rejects on load error/cancel/crash):
|
||||
no pass is ever queued inside a worker a restart is about to terminate —
|
||||
its transferred audio buffer would die with it. The worker serialized
|
||||
passes on the load anyway, so nothing starts later than before.
|
||||
2. **shader-f16 probe.** The worker's WebGPU probe also records
|
||||
`adapter.features.has("shader-f16")`; when the dtype that a WebGPU attempt
|
||||
would use requires fp16 (`dtypeRequiresF16`, pure + unit-tested) and the
|
||||
feature is absent, WebGPU is skipped outright — Firefox then loads
|
||||
straight into multithreaded WASM with no wasted attempt.
|
||||
3. **Live-on-WASM hint.** With live active on `wasm` and a heavy model
|
||||
(encoder ≥ 300 M params: medium, large-v3-turbo), the UI says the model
|
||||
runs slower than real time and suggests a smaller one — WASM lag there is
|
||||
inherent (D-024's fixed window cost), not a bug, so tell the user instead
|
||||
of looking frozen.
|
||||
**Alternatives rejected:** re-posting in-flight passes after respawn (their
|
||||
buffers are transferred — gone with the worker); a scout worker pre-flighting
|
||||
the accelerator (doubles accelerated-path load time to save the failure
|
||||
path); ort-web's proxy-worker mode to escape the pinned pool (undocumented
|
||||
interplay with JSEP).
|
||||
**Consequence:** the accelerator-failure path costs one worker respawn + a
|
||||
pipeline reload from cache (a few seconds, once per model+preference per
|
||||
session). A genuine both-attempts-fail load still reports its error
|
||||
normally. `deviceAttempts` is unchanged; the new policy pieces
|
||||
(`dtypeRequiresF16`, the `load-restart` message guard) are unit-tested in
|
||||
`tests/protocol.test.ts`. Real-Firefox verification (badge shows WASM with
|
||||
full threads, live keeps up on small models) still needs a manual run — same
|
||||
no-browser dev-env caveat as M2/M3.
|
||||
**Amendment (2026-07-17, same day — field report: tiny fixed, medium still
|
||||
slow in Firefox):** three observability/robustness additions to tell "slow is
|
||||
the CPU-vs-GPU physics" apart from "something is still degraded":
|
||||
1. The probe **skips software (fallback) WebGPU adapters**
|
||||
(`isFallbackAdapter`, both spec locations) — SwiftShader/llvmpipe/WARP
|
||||
would *succeed* at session creation and then run slower than multithreaded
|
||||
WASM, and the fallback chain only reacts to failures, not slowness.
|
||||
onnxruntime-web itself uses whatever adapter it is handed (verified in
|
||||
1.26.0-dev: no fallback-adapter or Firefox guard).
|
||||
2. The `ready` message now carries **`threads`** — read back from
|
||||
`env.backends.onnx.wasm.numThreads` *after* session creation, so ort's own
|
||||
clamp-to-1 (missing isolation) is visible. The footer shows it:
|
||||
"WASM (on-device, 11 threads)".
|
||||
3. The worker **logs the device chain** (`[whisper] …` console lines): each
|
||||
skipped backend (software adapter, missing shader-f16), each failed
|
||||
attempt with its error, and the final device+threads — previously fallback
|
||||
errors were swallowed silently, making field reports unactionable.
|
||||
Expected healthy Firefox numbers for medium q4 on WASM (from the D-023/D-024
|
||||
measurements): ~4 s per 30 s window at 12 threads, ~9–12 s at 4 — i.e. a
|
||||
14 s clip in ~10 s. Chromium's GPU path is a further ~3–5× faster; that
|
||||
remaining gap is inherent until Firefox's WebGPU works with ort-web's JSEP
|
||||
EP. If the badge shows 1 thread or the console shows a surviving software
|
||||
adapter, it is a bug in this decision's scope instead.
|
||||
|
||||
## D-026 — "Auto" never picks WebGPU on Firefox
|
||||
**Status:** Accepted (2026-07-17)
|
||||
Field-verified on Firefox (D-025's observability build): explicit
|
||||
Backend = WASM runs medium with a full thread pool and healthy speed, while
|
||||
explicit Backend = WebGPU (`device=webgpu wasmThreads=1`, correct per D-023)
|
||||
**completes but runs far slower** than the same machine's multithreaded WASM.
|
||||
That is the worst case for D-017's failure-driven fallback chain: ort-web's
|
||||
WebGPU EP (Chromium-developed; no Gecko handling in 1.26.0-dev, verified)
|
||||
creates the session *successfully* on Firefox, so nothing ever falls back —
|
||||
"auto" happily selects the slowest available backend.
|
||||
**Fix:** the worker's capability probe skips the WebGPU probe entirely when
|
||||
the preference is `"auto"` and the UA is Firefox (`/Firefox\//` on
|
||||
`navigator.userAgent`), with a `[whisper]` console line saying why. Firefox
|
||||
auto therefore resolves straight to multithreaded WASM (no accelerator
|
||||
attempt → no D-025 restart either). An **explicit** WebGPU selection is still
|
||||
honored — that is the deliberate retest hatch: when a future
|
||||
Transformers.js/ort-web bump gains real Gecko WebGPU support, verify via the
|
||||
explicit path, then delete the UA check.
|
||||
**Why UA sniffing:** there is no feature to detect — the sessions create and
|
||||
run; only throughput distinguishes them, and a benchmark probe would cost
|
||||
more than it saves. The check is one line, logged, scoped to `"auto"`, and
|
||||
targets the one engine with field evidence.
|
||||
**Revisit:** on any onnxruntime-web bump — retest explicit WebGPU on Firefox
|
||||
(file `tests/live.md` note) and drop the check when it reaches parity.
|
||||
|
||||
@@ -247,6 +247,14 @@ Notes (M5):
|
||||
transcription start (file drop / live start) or via an explicit
|
||||
per-model Download button in `ModelPicker` — and never when the engine
|
||||
toggle is set to "server".
|
||||
- [x] Firefox performance: never run the WASM fallback single-threaded after
|
||||
a failed accelerator attempt (worker posts `load-restart`, UI respawns
|
||||
with explicit wasm — D-025); skip WebGPU when fp16 weights need the
|
||||
absent `shader-f16` feature; hint that medium/large lag in live mode on
|
||||
WASM. Field-verified: WASM now gets the full thread pool; ort-web's
|
||||
WebGPU EP on Firefox completes but runs far slower than WASM, so
|
||||
"auto" skips WebGPU on Firefox (D-026, explicit selection still
|
||||
honored — retest on ort bumps).
|
||||
- [ ] Basic a11y pass (keyboard, focus, aria on progress/live transcript)
|
||||
- [ ] Production build + `bun start` deployment check; README for deployers
|
||||
|
||||
|
||||
Reference in New Issue
Block a user