Skip to content

Spec 0008 — findings & go/no-go

The interpretation of REPORT.md (a captured run; reproduce it on your own host with cmd/afmpeg-bench). Numbers below are from that run — linux/amd64, 8 CPUs, native ffmpeg 6.1.1, current n8.1.2 intermediate modules.

What the numbers say

  1. The WASM penalty is an encoder penalty, not a general one.
  2. Thumbnail (decode → scale → 1 frame) is only 2.4× native — decode and filtering in WASM are near-native.
  3. Transcode (encode-dominated) is 13–63× native, and the filter-heavy reel up to 150×.
  4. So the whole gap lives in the H.264 encoder — single-threaded, --disable-asm (no SIMD) — exactly the two levers that are structurally unavailable in WASM (0008 §1).

  5. Preset choice is the single biggest free lever. On transcode, x264 ultrafast is 13.1× native vs medium at 54.9× — a ~4× swing in WASM wall-clock (3.1 s vs 10.8 s) from one option, no code.

  6. openh264 is ~4× faster than libx264 in WASM (2.35 s vs 9.75 s on transcode) — so the LGPL default is also the faster encode path, at a quality/size cost.

  7. Instance-level parallelism works. A pool of 8 Runtimes clears an 8-job batch 5.0× faster than one Runtime — the only real parallelism absent WASM threads (0008 §4.1), and it delivers close to core-count scaling for batch throughput.

  8. Absolute times are small for small media. A 160p transcode is ~3 s at ultrafast; the in-memory/sandboxed path afmpeg was scoped to (0001 §9) is rarely the hot path (keyrx keeps native ffmpeg as its local fast path).

Go/no-go (R-AF-12)

Do not promote a dedicated perf-optimisation implementation spec now. The levers that matter are either free guidance or already expressible:

Lever (0008 §4) Verdict
Encoder/preset guidance Adopt now (docs). openh264 for the LGPL path; faster x264 presets. Biggest win per effort, zero code.
Instance-level parallelism (RuntimePool) Ready lever, gated on a batch consumer. 5× on 8 cores is real, but no consumer is throughput-bound today; a Runtime pool is already expressible by the caller. Promote to a first-class helper only when a batch/throughput consumer appears.
Build tuning (-Oz-O2/-O3) Not measured (needs a rebuilt module). A follow-up only if guidance + fleet prove insufficient.
Native backend (0028) The real answer for the genuinely encode/HW-bound consumer. A 50–150× encode gap is decisive when it matters; the CGO-free native driver (threads + SIMD + HW-accel) is the escape hatch — and is the next track already in flight.

Disposition: single-threaded WASM encode is 13–63× native (150× filter-heavy), but decode/ filter is ≈ native (2.4×). This is acceptable for afmpeg's scoped in-memory/sandboxed use with preset + encoder guidance; instance-level parallelism is the ready throughput lever (gated on a batch consumer); and the native backend (0028) is the escape for anyone genuinely perf- or HW-bound. R-AF-12 closes with these measurements as the rationale — no speculative optimisation spec is warranted.

Side observation (not a blocker)

In one early run at 640×480, the reel job's mixed-audio path (amix → aac) exited non-zero with aac … 2 frames left in the queue on closing (the audio encoder not fully drained at EOF). It did not reproduce at 320×240 (the reel succeeded on both encoders in the captured run), so it is an intermittent edge, not a confirmed defect — but worth an engine glance under 0026 (encoder-flush on filtered-audio EOF).