open-standards/wai/research/determinism-cost.md
Transaction Science 61759c51b5 wai: the int_hyper decode on the narrow kernel sets, measured; determinism-tiers §7 quotes it
research/determinism-cost.md §4.6, in the same record and by the same
procedure: the 192 payloads decoded again on every platform with every
kernel set it runs (15 new runs, 0 decodes differing from the record's),
and the record's time and energy runs repeated in one session per host
beside the record's own build and the float decoder. On x86_64 host A the
integer decode on the narrow kernels took 144.8 ms and 12.43 J of marginal
package energy at quality 6 against 210.7 ms and 14.03 J for the float
decode beside it (the record's build: 728.0 ms, 92.14 J). On the build
host its own CPU energy fell from 11.01 J to 6.43 J at quality 6 and is
still above the float decode's 1.28 J by that count. Host B's integer
decode was measured before and after, without a float decode beside it.
The scripts that took the readings are in runs/; decode-throughput.md
notes the new sets; the changelog records the change.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-25 04:06:15 -04:00

67 KiB
Raw Permalink Blame History

What exactness costs: wai.neural.int_hyper against its float model

Status. A measured record and its procedure, informative. Every figure below is computed from the readings in determinism-cost/record.json, and tools/wai_determinism_cost_verify.py recomputes each one from those readings and checks every figure this page and determinism-tiers §7 quote from it (each is followed by a det marker naming its place in the record). What WAI claims, and does not, on the strength of this record is in determinism-tiers §7. Measured 2026-10-22; the runs of §4.6, on the narrow kernel sets, 2026-10-24 and 2026-10-25.

1. What is measured, and what is not

wai.neural.int_hyper is an integer export of one pretrained learned codec: the mean-scale hyperprior mbt2018_mean (MSE), quantised to per-channel 12-bit weights and activations at 2¹², with an integer inverse GDN and an integer hyper-decoder that decides every entropy table (determinism-tiers §5). The float model is the same checkpoint, coded and decoded by the library its checkpoints are published with. This record measures what the integer decode costs against that float model, at all eight published qualities, on pictures of the pinned reference set:

  1. Rate and distortion: the BD-rate of the integer codec against the float codec, by PSNR and by MS-SSIM (§2.4).
  2. Reproducibility across platforms: each codec's streams decoded on every platform reached, and each decode compared byte for byte with the first platform's (§2.5).
  3. Time and energy of a decode, both codecs, on the CPU and on a GPU (§2.6).

Not measured here, and why:

  • wai.image.jpegai's exact decoder. It implements one configuration of the base standard, which gives one rate point per picture, so it has no rate-distortion curve to take a BD-rate over; and coding the reference set needs the standard's reference encoder, which was not run. Its one comparison with the reference decoder, on one codestream, is determinism-tiers §5 and §6.
  • wai.neural.int_synth and wai.neural.int_mlicpp. Neither has a float model with a rate axis to compare with: the committed int_synth vectors are a synthesis from a given latent (one of them a quantised float model trained on one photograph, the others seeded integer weights), and the int_mlicpp vector has seeded weights.
  • wai.neural.mbt2018_mean, WAI's float capability over the same model family, which decodes through an ONNX runtime. The float side here is the model's own library.
  • A neural processor. No decoder on main runs its graphs on one (determinism-tiers §5).
  • The encoder. Both codecs analyse the picture in floating point. The integer tier covers the decode: the bytes of a payload depend on the machine that encoded it, and every conforming sink then decodes those bytes to the same picture.

2. Procedure

2.1 Pictures

The pinned reference manifest wai-image-references.json (SHA-256 db33c663…6b554b66, the manifest tools/wai_visual_provenance_check.py pins) lists 384 files. The rule: every PNG entry the manifest marks self_test that holds one picture of 768×512 or 512×768 samples. That gives 24 pictures, each checked against the manifest's SHA-256 before it is read and named in the record by the first 16 hex digits of that digest. Both sides are multiples of 64, so every picture is coded whole.

2.2 The two codecs

  • Checkpoints. The eight published mbt2018_mean (MSE) checkpoints, qualities 1–8, each pinned by the SHA-256 of its bytes (CKPT_SHA256 in tools/wai_determinism_cost.py, and in the record) and loaded as tensors only. Qualities 1–4 have N = 128, M = 192; qualities 5–8, N = 192, M = 320.
  • Float codec. The library's own compress and decompress (release 1.2.8, on the pinned stack of tools/requirements-sota-int.txt): the float analysis, the factorised hyper-latent, the float hyper-decoder deciding each scale bucket, the float synthesis. Its reconstruction in [0, 1] becomes 8-bit samples by ×255 and rounding half to even.
  • Integer codec. The same model exported per quality exactly as tools/wai_sota_int_synth_export.py exports quality 6, coded as a WIH1 payload (integer-payloads §4), and decoded by wai_det_cost, which runs int_payload::decode_int, the integer decode path every reference sink runs. Two cross-checks tie the export to the one CI already checks, both run on the build host: the quality-6 set this procedure writes (g_s.whm, h_s.whm, entropy.json) is byte for byte the set the export tool writes; and on the export tool's pinned photograph (from tools/wai_sota_source_image.py), this procedure's integer encoder gives the export tool's hyper-decoder output and writes both rANS streams byte for byte as the export tool does.
  • Encoding. The library on one CPU thread with deterministic algorithms, on the build host (§3).

2.3 Rate and distortion

  • Rate: the bytes of the two entropy-coded streams, hyper-latent and latent, for both codecs, ×8 over the picture's samples per channel (bits per pixel). A WIH1 payload adds a 32-byte header and two 4-byte lengths, which are not counted; the record keeps each payload's size.
  • PSNR: 10·log₁₀(255²·n / SSE) over all n samples of the three channels, against the picture's 8-bit samples.
  • MS-SSIM, the perceptual metric: per channel on 8-bit samples (dynamic range 255), five scales, an 11-tap Gaussian window of σ = 1.5 applied over the valid region, K₁ = 0.01, K₂ = 0.03, 2×2 mean pooling between scales, the contrast-structure term at the first four scales and luminance-contrast-structure at the fifth, exponents 0.0448, 0.2856, 0.3001, 0.2363 and 0.1333, each term clamped at zero; the mean over the three channels. BD-rate reads it as −10·log₁₀(1 − MS-SSIM).

2.4 BD-rate

Per picture, the integer codec's eight (rate, quality) points against the float codec's eight, by the Bjøntegaard delta of ITU-T VCEG-M33: log₁₀ of the rate interpolated as a function of quality by a monotone piecewise-cubic Hermite interpolant (the Fritsch–Carlson slopes with the three-point shape-preserving ends), each curve integrated over the quality range the two share, and the mean difference in log rate turned into a percentage. A positive figure is the extra rate the integer codec needs for the float codec's quality. A curve whose quality does not rise strictly with rate has no BD-rate, and the record lists such pictures. The record states each picture's figure, and their median, mean, minimum and maximum.

2.5 Reproducibility across platforms

Every integer payload is decoded on each platform of §3, and each decode's RGB8 compared by SHA-256 with the build host's first decode of it. Every float stream is decoded again in a fresh process on each platform and device of §3, under the entropy tables the encoder used (their SHA-256 is recorded per platform, so a difference in the tables would show), and its RGB8, its decoded entropy-table indexes and its decoded symbols compared, each by SHA-256, with the encoding process's own decode. A decode whose indexes or symbols differ has diverged in the entropy decode; one whose symbols agree and whose samples differ has diverged after it. For every float decode that differs, the record keeps its PSNR change.

2.6 Time and energy

tools/wai_det_energy.py runs a decoder as a child process that loads its models and streams, decodes each once, and then decodes the same eight pictures (the first eight of §2.1, at quality 2 and at quality 6) in each of seven rounds on command, blocking between rounds. A round's time is the child's own clock around its decodes; every decode's output is hashed and compared with its first decode, so no time is reported for a different output. The counters, each with its scope:

  • The decoder's own CPU energy (macOS): the kernel's per-task energy count (ri_energy_nj, proc_pid_rusage), read while the child blocks and only once its CPU time has stopped moving, so every thread's energy has folded into the count. Scope: the decoding process, CPU only; it excludes the GPU. Class OnChipCounter, filtering unknown. Energy that system services spend on the process's behalf is billed to it in a separate count, which the record keeps beside it.
  • The GPU rail (macOS): IOReport's GPU Energy channel, the whole GPU, read every 100 ms. A round's GPU figure counts only where the decoder held at least 99% of the GPU time that every accelerator client accumulated over the round (the I/O Registry's accumulatedGPUTime), which the record keeps per round.
  • Package energy (Linux): powercap package-0, the whole package, read every 100 ms.

A whole-device counter is bracketed by 5 s idle windows before and after every round. A round's marginal energy is the counter's advance over the round minus the mean idle power of its two brackets times the round's span, and its ratio is the marginal power over the larger idle standard deviation (energy-measurement §3, §4). A round's figure is used only where the counter was live in the round and in both its brackets (energy-measurement §3.1: advancing in two intervals in a row, in five or more and in at least four of every five, over at least 300 ms) and its ratio is at least 3; a figure is publishable only where all seven rounds are used and read clean (ratio ≥ 10). The record gives every round's readings, and per run the median, minimum and maximum over rounds of the time and of each energy figure per decode, with the host's load average.

3. Platforms

platform machine what ran
build host arm64 desktop SoC, 24 performance and 8 efficiency cores, 80-core integrated GPU; macOS 27.0.1; shared, under heavy load from other work both encoders; the integer decode on the CPU (all cores, one core), on the GPU (Metal) and as wasm32-wasip2 under wasmtime 49.0.3; the float decode on the CPU (one thread, the library's default of 24) and on the GPU (Metal); the timed runs with the per-task count and the GPU rail
x86_64 host A x86_64 SoC, 16 cores and 32 threads, AVX-512, integrated GPU of PCI vendor 0x1002 (device 0x1586); Linux 7.0, glibc 2.43; shared, otherwise lightly loaded the integer decode on the CPU, on the GPU (Vulkan, Mesa 26.0.8) and as the build host's wasm32-wasip2 binary under wasmtime 49.0.3; the float decode on the CPU (one thread, the library's default of 16); the timed runs with the package counter
x86_64 host B x86_64 laptop CPU, 14 cores and 20 threads, AVX2, discrete GPU of PCI vendor 0x10de (device 0x28e1); Linux 7.0, glibc 2.39 the integer decode on the CPU and on the GPU (Vulkan, driver 595.84); the float decode on the CPU (the library's default of 14 threads) and on the GPU (through the GPU vendor's compute API, in the float stack's 2.14.0 build for it)

Builds: rustc 1.98.0, release profile, --no-default-features --features neural_int (with ops_gpu for the GPU decodes, wgpu 30.0.1); the wasm32-wasip2 binary was built once, on the build host, and the same file ran on both hosts. Float stack: Python 3.11 (3.12 on host B), the pinned versions of tools/requirements-sota-int.txt (its CPU-only build on host A, its GPU build on host B), with deterministic algorithms requested. The tags under which the record keeps each platform are in determinism-cost/runs/README.md.

§4.6's builds are the same, from the change that added the narrow kernel sets, made on each host (the wasm32-wasip2 file again once, on the build host), beside each host's build of the record's own commit; on host A the float stack was set up again from the same pinned versions. Host B's GPU was not reached for §4.6 (its driver was not loaded when the runs were taken), so its GPU decode is the record's only.

4. Results

Every figure here is the record's, and the verifier of §6 recomputes it; each is followed by a marker naming its place in record.json, and wai_determinism_cost_verify.py holds the text to the record.

In brief:

  • Rate. At equal PSNR the integer codec needs a median of +1.61 % more rate than the float codec over the 24 pictures, and at equal MS-SSIM a median of +0.95 %. The smallest figure of any picture is +0.56 % by PSNR and +0.52 % by MS-SSIM: the export costs rate on every picture (§4.1, and each picture's figure in the record).
  • Bytes. The integer decode gave the same bytes on every platform, under every kernel set: 0 of 4608 decodes differed (§4.3, §4.6). The float decode gave the encoder's bytes only on the encoder's own CPU. Everywhere else it gave other bytes for every stream, and its entropy decode diverged on many of them, from 76 of 192 on host B's CPU to 192 on its GPU (§4.4).
  • Time and energy. On host A, where every round read clean, the record's integer decode took longer and drew more package energy than the float decode on the same CPU, at both qualities, and so did the integer decode with its graphs on the integrated GPU (§4.5). On the narrow kernel sets (§4.6) the same integer decode, giving the same bytes, took 144.8 ms and 12.43 J at quality 6 against 210.7 ms and 14.03 J for the float decode measured beside it, and less time and energy than the float decode at quality 2 as well. On the build host it still drew more of its own CPU energy than the float decode (§4.6, and §5 on what that count covers there).

4.1 BD-rate, integer codec against float codec

quality metric pictures median mean min max
PSNR 24 +1.61 % +1.71 % +0.56 % +2.97 %
MS-SSIM 24 +0.95 % +1.03 % +0.52 % +2.45 %

4.2 Per quality (medians over the pictures)

quality float bpp float PSNR (dB) integer rate ÷ float rate ΔPSNR (dB), integer − float ΔMS-SSIM (dB), integer − float
1 0.106 28.16 1.0006 -0.005 -0.005
2 0.166 29.74 1.0000 -0.007 -0.007
3 0.248 31.36 1.0000 -0.016 -0.010
4 0.370 32.99 1.0001 -0.032 -0.018
5 0.550 34.78 1.0000 -0.022 -0.010
6 0.792 36.79 1.0000 -0.174 -0.073
7 1.106 38.79 1.0000 -0.176 -0.108
8 1.511 40.78 1.0000 -0.942 -0.481

4.3 The integer decode across platforms

Tags (§3): macos-arm64-cpu the build host's CPU, all cores, and -1t one core; macos-arm64-gpu-metal its GPU; wasm32-wasip2 the WebAssembly build under wasmtime there, and wasm32-wasip2-x86_64 the same file on host A; linux-x86_64-cpu and linux-x86_64-gpu-vulkan host A's CPU and integrated GPU; linux-x86_64-b-cpu and linux-x86_64-b-gpu-vulkan host B's CPU and discrete GPU. Each line's decodes are the 192 payloads, 24 pictures at 8 qualities.

platform decodes identical to the build host's CPU decode differ refused
linux-x86_64-b-cpu 192 192 0 0
linux-x86_64-b-gpu-vulkan 192 192 0 0
linux-x86_64-cpu 192 192 0 0
linux-x86_64-gpu-vulkan 192 192 0 0
macos-arm64-cpu 192 192 0 0
macos-arm64-cpu-1t 192 192 0 0
macos-arm64-gpu-metal 192 192 0 0
wasm32-wasip2 192 192 0 0
wasm32-wasip2-x86_64 192 192 0 0

4.4 The float decode across platforms

Against the encoding process's own decode. Tags: macos-arm64-cpu-1t and macos-arm64-cpu the build host's CPU with one thread and with the library's default of 24, each in a fresh process; macos-arm64-gpu-mps its GPU; linux-x86_64-cpu and linux-x86_64-cpu-1t host A's CPU with the library's default of 16 threads and with one; linux-x86_64-b-cpu host B's CPU (14 threads); linux-x86_64-b-cuda host B's GPU. Where the symbols differ, the entropy decode diverged.

platform decodes identical to the encoder's decode differ of which the indexes differ of which the symbols differ ΔPSNR where they differ (dB): median, min, max ΔPSNR where the symbols differ (dB): median
linux-x86_64-b-cpu 192 0 192 76 76 -0.000, -30.126, +0.000 -17.582
linux-x86_64-b-cuda 192 0 192 192 192 -19.697, -30.518, -9.500 -19.697
linux-x86_64-cpu 192 0 192 183 183 -17.037, -30.686, +0.000 -17.434
linux-x86_64-cpu-1t 192 0 192 182 182 -17.037, -30.457, +0.000 -17.345
macos-arm64-cpu 192 192 0 0 0 — —
macos-arm64-cpu-1t 192 192 0 0 0 — —
macos-arm64-gpu-mps 192 0 192 184 184 -17.200, -34.147, +0.000 -17.358

4.5 Time and energy per decode (median over rounds; min–max)

Eight pictures per round, seven rounds; the runs' labels give the host (linux is host A, macos the build host), the codec, the fabric and the quality. The own CPU energy is the per-task count (macOS); the whole-device figure is the package counter (rapl, host A) or the GPU rail (ioreport-gpu, the build host), as the round's marginal over its idle brackets, from the rounds used (§2.6). The build host's rounds ran under the load average shown.

run time (ms) own CPU energy (J) whole-device marginal energy (J) whole-device rounds load average
linux-float-cpu-q2 123.9 (108.2–134.3) — rapl: 9.1 (8.0–9.5) 7 of 7 used; 7 live; 7 clean 2–3
linux-float-cpu-q6 191.5 (181.0–200.3) — rapl: 13.7 (13.3–14.0) 7 of 7 used; 7 live; 7 clean 5–7
linux-int-cpu-q2 347.3 (346.4–348.6) — rapl: 42.4 (42.1–42.5) 7 of 7 used; 7 live; 7 clean 1–4
linux-int-cpu-q6 729.6 (728.7–731.6) — rapl: 92.5 (92.2–92.7) 7 of 7 used; 7 live; 7 clean 3–11
linux-int-gpu-q2 378.5 (377.2–379.7) — rapl: 41.9 (41.0–43.4) 7 of 7 used; 7 live; 7 clean 1–3
linux-int-gpu-q6 845.4 (844.1–846.2) — rapl: 93.5 (91.7–93.8) 7 of 7 used; 7 live; 7 clean 1–5
macos-float-cpu-q2 714.2 (209.0–5663.9) 0.7 (0.7–0.7) — — 45–199
macos-float-cpu-q6 403.4 (214.6–736.4) 1.2 (1.1–1.5) — — 85–126
macos-float-gpu-q2 51.7 (50.0–55.3) 0.1 (0.1–0.1) ioreport-gpu: none usable 0 of 7 used; 0 live; 2 clean, 5 no ratio; GPU share 1.000–1.000 126–149
macos-float-gpu-q6 66.4 (60.0–77.8) 0.2 (0.1–0.2) ioreport-gpu: none usable 0 of 7 used; 0 live; 4 no ratio, 3 clean; GPU share 0.113–1.000 74–81
macos-int-cpu-q2 152.4 (115.1–376.8) 4.6 (4.4–4.8) — — 44–57
macos-int-cpu-q6 648.9 (519.5–865.7) 10.5 (10.2–10.9) — — 113–130
macos-int-gpu-q2 251.3 (248.7–253.5) 0.1 (0.1–0.1) ioreport-gpu: none usable 0 of 7 used; 5 live; 7 clean; GPU share 0.788–0.916 117–128
macos-int-gpu-q6 529.9 (527.9–546.0) 0.1 (0.1–0.1) ioreport-gpu: none usable 0 of 7 used; 0 live; 2 clean, 5 no ratio; GPU share 0.923–1.000 37–68

On the build host no GPU-rail figure is used. While the GPU idled between rounds its rail did not advance at all, so the idle brackets around most rounds were not live (§2.6); and in some rounds another client held more than 1% of the GPU's time (the table's GPU share). Those rounds' readings are in the record, and nothing is drawn from them. The per-task counts there are the decoding process's own CPU energy, which the load on the host changes less than it changes the time, but does change; §5 states what they cover.

4.6 The integer decode on the narrow kernel sets

The record's integer decoder (§4.3, §4.5) ran the wide kernels of wai-rs's int_kernels: 32×32→64-bit multiply-accumulates, NEON on arm64 and portable Rust elsewhere. The runs of this section are of the build that added the narrow kernel sets (int_kernels::Isa): convolutions on 16-bit operands with 32-bit partial sums in blocks bounded so that no value leaves its type (NEON on arm64; AVX2, AVX2 with VNNI, AVX-512 and AVX-512 with VNNI on x86_64), the inverse GDN's norms and outputs in SIMD lanes, and the element-wise operators and the requantisation in 64-bit arithmetic where their values fit. Every operator still gives the interpreter's integers (the int_kernels documentation states how, and the tests it names hold each set to the interpreter); the set is chosen by the processor's features, or by WAI_INT_ISA. The wasm32-wasip2 build has no narrow set and runs the wide kernels, as wide names below, with the build's other changes.

Bytes. Every payload decoded again on each platform with each kernel set the platform runs, against the record's build host CPU decode (the GPU decodes run both graphs on the device and are unchanged by the kernel sets; the runs check it):

platform kernel set decodes identical to the build host's CPU decode differ refused
linux-x86_64-b-cpu avx2 192 192 0 0
linux-x86_64-b-cpu avx2-vnni 192 192 0 0
linux-x86_64-b-cpu wide 192 192 0 0
linux-x86_64-cpu avx2 192 192 0 0
linux-x86_64-cpu avx2-vnni 192 192 0 0
linux-x86_64-cpu avx512 192 192 0 0
linux-x86_64-cpu avx512-vnni 192 192 0 0
linux-x86_64-cpu wide 192 192 0 0
linux-x86_64-gpu-vulkan avx512-vnni 192 192 0 0
macos-arm64-cpu-1t neon 192 192 0 0
macos-arm64-cpu neon 192 192 0 0
macos-arm64-cpu wide 192 192 0 0
macos-arm64-gpu-metal neon 192 192 0 0
wasm32-wasip2 wide 192 192 0 0
wasm32-wasip2-x86_64 wide 192 192 0 0

Time and energy. The procedure of §2.6, on the same eight pictures. On each host the runs were taken in one session: -before the record's own build of the integer decoder run again, -after this section's build (its kernel set chosen by the processor: AVX-512 with VNNI on host A, AVX2 with VNNI on host B, NEON on the build host; -after-avx2 the AVX2 set on host A), -again the float decoder run again. linux-b is host B, measured only for the integer decode (its float stack was not set up again), under the load of the CI jobs it also serves.

run time (ms) own CPU energy (J) whole-device marginal energy (J) whole-device rounds load average
linux-b-int-cpu-q2-after 170.2 (168.9–203.6) — rapl: 11.56 (9.31–11.91) 7 of 7 used; 7 live; 2 relative-only, 5 clean 5–6
linux-b-int-cpu-q2-before 714.0 (704.2–716.3) — rapl: 43.84 (42.71–44.30) 7 of 7 used; 7 live; 7 clean 2–9
linux-b-int-cpu-q6-after 294.3 (292.3–301.1) — rapl: 19.97 (18.83–20.20) 7 of 7 used; 7 live; 7 clean 6–11
linux-b-int-cpu-q6-before 1582.1 (1555.6–1598.9) — rapl: 84.92 (83.95–87.95) 7 of 7 used; 7 live; 7 clean 6–12
linux-float-cpu-q2-again 132.2 (123.2–139.7) — rapl: 9.26 (9.00–9.33) 7 of 7 used; 7 live; 7 clean 2–4
linux-float-cpu-q6-again 210.7 (200.8–226.1) — rapl: 14.03 (13.65–14.41) 7 of 7 used; 7 live; 7 clean 6–8
linux-int-cpu-q2-after 92.9 (90.8–93.1) — rapl: 7.31 (7.29–7.50) 7 of 7 used; 7 live; 7 clean 3–5
linux-int-cpu-q2-after-avx2 101.6 (101.3–103.0) — rapl: 9.27 (9.22–9.35) 7 of 7 used; 7 live; 7 clean 3–5
linux-int-cpu-q2-before 345.8 (345.2–346.4) — rapl: 42.25 (42.17–42.33) 7 of 7 used; 7 live; 7 clean 2–5
linux-int-cpu-q6-after 144.8 (142.7–147.7) — rapl: 12.43 (12.28–12.58) 7 of 7 used; 7 live; 7 clean 8–11
linux-int-cpu-q6-after-avx2 166.3 (164.7–166.8) — rapl: 16.46 (16.36–16.53) 7 of 7 used; 7 live; 7 clean 2–4
linux-int-cpu-q6-before 728.0 (725.8–729.8) — rapl: 92.14 (91.96–92.67) 7 of 7 used; 7 live; 7 clean 4–12
macos-float-cpu-q2-again 212.5 (140.4–330.5) 0.92 (0.81–1.07) — — 40–47
macos-float-cpu-q6-again 271.3 (215.7–598.7) 1.28 (1.22–1.51) — — 46–61
macos-int-cpu-q2-after 274.1 (98.3–385.0) 2.96 (2.83–3.12) — — 32–46
macos-int-cpu-q2-before 130.9 (106.2–338.3) 4.76 (4.47–4.90) — — 34–42
macos-int-cpu-q6-after 477.5 (392.1–909.3) 6.43 (6.12–6.63) — — 32–62
macos-int-cpu-q6-before 268.1 (228.4–409.0) 11.01 (10.93–11.16) — — 33–47

On host A every round of every run read clean. At quality 6 the narrow kernels took the integer decode from 728.0 ms and 92.14 J to 144.8 ms and 12.43 J, against 210.7 ms and 14.03 J for the float decode; at quality 2 from 345.8 ms and 42.25 J to 92.9 ms and 7.31 J, against 132.2 ms and 9.26 J. With the AVX2 set on the same host the integer decode drew 9.27 J at quality 2 and 16.46 J at quality 6. The record's own runs of 2026-10-22 (§4.5) and the -before runs agree within a few tenths of a joule.

On host B, under the load of the CI jobs it serves, the integer decode went from 1582.1 ms and 84.92 J to 294.3 ms and 19.97 J at quality 6, and from 714.0 ms and 43.84 J to 170.2 ms and 11.56 J at quality 2, where two of the seven rounds were relative-only against their idle brackets and the rest clean. No float decode was measured beside them there.

On the build host the narrow kernels lowered the decoder's own CPU energy from 11.01 J to 6.43 J at quality 6 and from 4.76 J to 2.96 J at quality 2. The float decode's own CPU energy there was 1.28 J and 0.92 J: by that count the integer decode still costs more on this host, and the count may leave out the float decode's matrix unit (§5). The host was under heavy load from other work throughout (the load averages in the table). Its times swing with that load from round to round and from run to run by more than the kernels change them, so that a run's median time there says little: the -after runs' medians lie above the -before runs' though their ranges overlap, while each run's per-task counts stay within a few tenths of a joule of each other. The per-task count is the build host's comparable figure.

5. What this does and does not show

  • One model, one export. The integer codec is a post-training quantisation of one model family with one recipe: per-channel 12-bit weights, activations at 2¹², no retraining. Another model, a wider export or quantisation-aware training costs something else, and this record does not say what.
  • One set of pictures. 24 pictures of one source set at 768×512, whole. Rate and quality are picture-dependent, so the record keeps every picture's figure.
  • What the BD-rate includes. The integer codec's rate is its own: its hyper-decoder decides the tables in integers, so its streams differ from the float codec's even before its synthesis runs. The BD-rate is the cost of the whole integer codec at equal quality, rate and distortion together.
  • Reproducibility. An integer decode that gave the same bytes on every platform reached is evidence of the construction on those platforms. It is not the ground of the tier's claim (determinism-tiers §7). A float decode that reproduced on a platform here says nothing of another platform, another build of the library or another device.
  • Time and energy. Both hosts are shared, and the build host was under heavy load from other work throughout; the record keeps the load average of every round. The per-task count is the decoder's own CPU energy and nothing else; the GPU rail and the package counter cover their whole device, and a figure from them is the round's marginal over its idle brackets. The record's integer decoder is CPU code with the wide kernels of int_kernels (NEON on arm64, portable Rust elsewhere), §4.6's with the narrow kernel sets (NEON on arm64, AVX2 and AVX-512 on x86_64), and its GPU decode runs the graphs the device takes through wai-ops-gpu; the float decode runs the library's own kernels. The figures compare those implementations on those hosts, not integer and float arithmetic in general.
  • What the per-task count covers on the build host. It is the kernel's account of the process's CPU energy. The float library's CPU kernels on that host may hand matrix work to a matrix unit the CPU clusters share, and this record does not establish whether the per-task count includes that unit's energy; so on the build host, the float codec's CPU figure may be lower than the energy its decode caused. Host A's package counter has no such gap for the CPU: it covers the whole package, the decode's share of it being the marginal over the idle brackets. Whether it also covers the package's integrated GPU, the interface does not state, so host A's figure for the integer decode with its graphs on that GPU is the package's marginal and nothing more.
  • The encoder is float in both codecs and is not measured.

6. Reproduce

W=/some/work/dir; T=wai/tools
python3 $T/wai_determinism_cost.py select  $W <reference-root>   # 24 pictures, checked
python3 $T/wai_determinism_cost.py export  $W                    # 8 pinned checkpoints -> integer sets
python3 $T/wai_determinism_cost.py encode  $W                    # both codecs; float decode in-process
python3 $T/wai_determinism_cost.py jobs    $W native > jobs.tsv
wai_det_cost decode jobs.tsv > $W/int.native.tsv                 # the integer decodes (wai-rs bin)
python3 $T/wai_determinism_cost.py metrics $W
# per platform: wai_det_cost [--gpu] decode <jobs> > $W/xplat/<tag>.tsv (+ <tag>.json, the platform)
#               wai_determinism_cost.py fdecode $W <tag> --device cpu|mps [--threads 1]
# per run:      wai_det_energy.py --label L --counter task|ioreport-gpu|rapl -- <child> > $W/energy/L.json
python3 $T/wai_determinism_cost.py record $W wai/research/determinism-cost/record.json
python3 -I $T/wai_determinism_cost_verify.py wai/research/determinism-cost/record.json \
    wai/research/determinism-cost.md wai/extensions/determinism-tiers.md

The scripts that ran each step on each host are in determinism-cost/runs/.