Long-term charter: PLAN.md — the science + training plan for being the best
compact CPU engine at upscaling libjpeg-turbo/mozjpeg-encoded images (exact-encoder training,
quant-table conditioning via zenjpeg, fidelity-first). Model/runtime survey that seeded this:
~/work/zen/SR-MODELS-RUNTIMES-2026-07-21.md. Two crates today:
zensr-bench— tract-onnx CPU bench for fixed-shape SR ONNX exports (tools/export_ntire25.pyexports NTIRE2025_ESR zoo checkpoints from~/work/superrez/NTIRE2025_ESR; ONNX files land in gitignoredmodels/).zensr-micro(+zensr-micro-abicdylib) — hand-rolled SPANF (fc=32, x4) inference in safe Rust; FFI unsafe isolated in the -abi crate (zentract-abi pattern). Op set: grouped/plain conv3x3, conv1x1, SiLU, sigmoid gate, concat, PixelShuffle(4).
The shipped pipeline (zensr-zenjpeg::restore_jpeg): probe → decode policy
(Knusperli only Annex-K q≤9.5) → guarded ×1 model → S10 quantization-consistency
projection (output provably re-encodes to the file's own coefficients; family-
calibrated slack + absolute sample-quantization slack) → optional ×2 SR.
| tier | model | params / f16 size | ssim2 gain q15/35/55/75/90 | s/MP @12T |
|---|---|---|---|---|
| quality (default) | dejpeg7_graphics | 595k / 1.16 MB | +7.78/+6.11/+4.60/+3.19/+1.06 | 5.3 |
| realtime | dejpeg_rt24g (distilled, 200k steps) | 43k / 84 KB | +5.07/+4.44/+3.26/+2.85/+0.87 = 47–71% of quality tier | 0.16 |
| low-q graphics route | dejpeg9_gfxycc | 595k / 1.16 MB | +2.72/+1.45/+1.09/+0.78/+0.21 OVER dejpeg7 on graphics (paired, 81/81/89/81/67% of files); negative on photographs from q55 up — route it, do not default to it | 5.3 |
Re-measured 2026-09-08 on the canonical corpus. The previous figures (+10.65/+5.64/+3.53/+2.03/+0.95 and +6.88/+3.36/+2.02/+1.15/+0.32) were produced against the wrong imazen-26 and a split that was never a split; both are gone. The models held up — they are lower at q15 and higher at q35–q90 than before — but no old number was reproducible, so all of them are replaced rather than defended. Method and the full comparison:
benchmarks/rescore_canonical_2026-09-08.md. Note also thatdejpeg7_graphicsinherits from a fine-tune chain that trained on 20 corrupted ground-truth pairs (docs/MODEL-PROVENANCE-AUDIT.md); the committed realtime weights are outside that lineage, and a retrain is queued for the corpus swap regardless.
Both tier rows: canonical imazen-26 (github.com/imazen/imazen-26),
held-out split (validate ∪ test of eval_split/imazen26_effective_split.tsv,
0 training files scored), PNG references, libjpeg-turbo 4:2:0, per-file
median gain over the plain decode, n=36/cell across 21 content classes, shipped
pipeline output (post-projection). Win fractions 97/100/100/94/84%
(dejpeg7) and 98/100/94/84/67% (rt24g) on the full 63-file sample. Rows:
benchmarks/clean_ladder_2026-08-03.pointer.md.
Corrected 2026-08-03, in two opposite directions. Absolute gains were understated — the previous +7.1/+5.0 at q15 were measured against a partly JPEG-sourced eval split, which penalised the model for removing artifacts that were present in the reference. But realtime-as-a-fraction-of-quality was overstated: measured 57–65%, against the published 68–79%. "The contamination understated everything" is too simple.
Runtime: safe-Rust explicit SIMD throughout — one #[magetypes] f32x8 kernel
family (AVX2/SSE4.2/NEON/WASM128/scalar) + a hand _v4x AVX-512 f32x16 variant,
packed weights, tiled multithreading with exact halos, strided rows. Measured
efficiency: realtime tier ~410 GFLOPS aggregate @12T (near practical peak);
quality tier ~225 GFLOPS (cache-bound at nf=64 — open rungs: strip-fused layer
scheduling, Winograd F(2,3)). No GPU path yet (CubeCL spike queued).
Routing (all probe-driven): chooser p(graphics)>0.85 ∧ q≤60 → gfxycc; identity gate at q≥94.5 at 4:2:0 and q≥88 at 4:4:4 (equivalently, Butteraugli distance ≤0.6 / ≤1.3) — the same nominal quality is a less damaged image at 4:4:4, measured as clean at q90 there as q94 at 4:2:0, and left ungated the model lost up to 2.1 ssim2 with 91% of files harmed; chain ×1 before SR when 4:2:0 ∨ q≲50 (chain +1.9 ssim2 at 420 q35; skip at 444 high-q).
Above that gate, estimate_gain predicts the median ssim2 a restore pass would
buy, and Routing::Auto { min_gain } (default 0.25) skips the pass when it is
not worth the cycles. Validated on clean references 2026-08-03: best of six
rules on held-out images, +0.17 ssim2 over restoring everything while skipping
21% of the work. IJG-family and distance-quantised encoders use separate
measured curves — cjpegli and zenjpeg quantise on butteraugli distance, and
converting that onto the IJG quality axis was optimistic in 39 of 40 cells and
wrong-signed in 9 (benchmarks/clean_ladder_jpegli_2026-08-03.md).
The per-tier threshold (identity above q82 for the 84 KB realtime model) lives
in the zenjpeg integration branch's RestoreOptions::realtime_tier, not in
this repo — policy_high_q_identity here is tier-independent. Ship format is
f16 (measured ≤9e-4 output delta ≈ ¼ of an 8-bit step; goldens verify through
f16; int8 weights-only PTQ measured catastrophic — needs QAT). Every model dir
carries an autogenerated repro.sh + full provenance meta.
Key falsifications (full registry in SYSTEMS.md): input conditioning (scalar & dmap), YCbCr-native as general pipeline (3 designs incl. de-confounded scratch pair), lattice-aware chroma arch above q35 (oracle+lattice decomposition), guided chroma upsampling (GT-guide JBU loses to bilinear), zero-bias weight compression for conv kernels, Boundary4Tap at any q.
We ship output distillation: the student trains on quality-tier teacher outputs instead of GT (S9). Measured: +0.41 ssim2 paired over direct GT training at 113k, and the recipe saturates at 43k params (rt24d == rt32d quality at 3.2× speed). A zenpapers sweep (500-record SR manifest; 14 KD papers 2019–2026: IMDN/RFDN lineage, FAKD-style feature distillation, DVMSR, DSCLoRA, edge GAN-KD, INT8 teacher-guided QAT) shows feature/affinity KD claims of small additional gains on tiny students — but our student already saturates its capacity under output-KD, so feature-KD is queued as a falsification rung, not adopted. What makes ours documented: paired same-seed controls, committed TSVs, and per-model repro scripts.
| Thing | Number |
|---|---|
| SPANF x4 via tract 0.23.4 | 3.27 MP-out/s (128², 80 ms) · 3.33 (256², 315 ms) |
| TSR / EFDN / NanoSR via tract | 2.0 / 1.1 / 1.25 MP-out/s (128²) |
| micro-v4x vs tract, interleaved paired bench (quiet box) | 128²: 57.6 vs 71.5 ms (1.24× faster); 256²: 233 vs 292 ms (1.25×); 64²: 18.0 vs 16.2 ms (near-parity) |
| AVX-512 v4x vs AVX2 v3 (same kernels) | 1.33–1.42× speedup |
| Multithreaded tiling (512²→2048², exact 16-px halo) | 15.5 MP-out/s @8 threads · 17.8 @16 · 18.2 @24 (230 ms for a 16.8 MP ×4 output; tile 112-128) |
| zensr-micro-abi cdylib, scalar-only | 271,432 B (265 KB) |
| zensr-micro-abi cdylib, full SIMD dispatch | 413,936 B (404 KB) (post-recovery, unfused kernels + gate) (v4x AVX-512 + v3 AVX2 + mandatory scalar fallback; tier_v4 feature off by default; incant! always links scalar) |
| correctness vs PyTorch golden (every tier v3/v4/v4x) | max_abs 8.6e-6 (PASS) |
| f16 weights (297,696 B) | 59.2 dB ramp / 75–76 dB photo PSNR vs fp32 — TRANSPARENT, ship it |
| int8-pc weights (152,400 B) | 16.8 dB ramp / 35–36 dB photo — NOT viable (see below) |
| SPANF weights fp32 | 593,152 B |
Full grids: benchmarks/tract_cpu_2026-07-22.tsv, benchmarks/quant_accuracy_2026-07-22.tsv.
- f16: production-safe. Worst case 0.3% max-error on photos, 2.5% of pixels shift ±1 u8 step
(75-76 dB). Rust decoder (safe bit-twiddle, subnormal-correct) in
decode_f16_weights. - int8 post-training quantization: broken for SPANF (35 dB photo, 90%+ u8 mismatch on ramp). The gate-exemption probe (quantize all but the σ-gate-feeding convs) changed nothing — sensitivity is distributed across the conv chain, so mixed-precision doesn't rescue it. int8 for SPAN-class requires QAT (MAI recipe) or an int8-by-design arch (ABPN/ECBSR/ETDS).
- int6/int4/k-means-256 codebook: 5-21 dB, dead on arrival.
- One
#[magetypes(v3, neon, wasm128, scalar)]family (f32x8) + hand#[arcane]_v4x(and feature-gated_v4) on native 512-bit f32x16; kernels macro-instantiated per width; whole forward inlines into one target_feature region per tier;incant!dispatches once per call. - magetypes gotcha (root-caused here):
recip()is a Newton-refined approximate reciprocal —recip(inf) = NaN(inf·0 in the refinement step). Sigmoid afterexp_midpsaturation MUST use exact division. Real SPANF pre-activations reach ~±100, so exp(-v) overflowing to inf is by design, not an anomaly. Symptom was 97% NaN output whilef32::max-based diff stats looked clean — always NaN-check in diff stats. - Archmage tier map (current, 0.9.28): v3 = AVX2+FMA (native 256), v4 = AVX-512 base,
v4x = AVX-512 extended;
F32x8Convert(transcendentals bound) is NOT implemented for v4/v4x tokens — use f32x16 there.
Paired per file — median of spanf − lanczos over images, with the fraction of
files each metric improves. Not the difference of per-method medians, which
compares each image against the other method's distribution and reports
losses that no image experienced.
| subcorpus | ΔPSNR | win | ΔSSIM2 | win | Δbutteraugli n3 | win |
|---|---|---|---|---|---|---|
| documents | +2.44 dB | 1.00 | +26.86 | 1.00 | −3.354 | 1.00 |
| maps | +2.16 dB | 1.00 | +11.55 | 1.00 | −1.548 | 1.00 |
| art-scans | +0.54 dB | 0.75 | +12.63 | 0.88 | −0.499 | 0.62 |
| textures | +0.46 dB | 1.00 | +8.13 | 0.62 | −0.690 | 0.75 |
| screen | +0.51 dB | 0.75 | +6.69 | 0.75 | −0.599 | 0.88 |
| people | +0.77 dB | 1.00 | +6.38 | 1.00 | −0.609 | 0.88 |
| photos | +1.16 dB | 1.00 | +5.49 | 0.88 | −0.702 | 1.00 |
| renders | +0.92 dB | 1.00 | −1.81 | 0.38 | −0.924 | 0.88 |
| all | +0.90 dB | 0.94 | +8.45 | 0.81 | −0.837 | 0.88 |
(Butteraugli: negative is better.)
SPANF wins 8/8 subcorpora on PSNR and butteraugli, 7/8 on SSIM2, dominating
text and line content. renders is the one disagreement: PSNR improves on all 8
files and butteraugli on 7, while SSIM2 says the median image gets worse — a
metric disagreement on synthetic content, not a demonstrated regression.
References are 100% PNG (/mnt/v/imazen-26-clean), files pinned to
eval_split/imazen26_eval_files.tsv. (Both are gone: the 2026-09-08 corpus
repoint replaced the pin with the canonical split, and imazen-26-clean derived
from the deleted acquisition root and has not been rebuilt. Numbers below predate
the repoint and are provisional. docs/CORPUS-REPOINT-IMPACT.md.) Harness: eval bin (zenpng/zenjpeg
decode → CatmullRom ×4 down → {spanf, lanczos, catmullrom} up →
psnr/ssimulacra2/butteraugli-n3 vs HR). Caveat: LR degradation is linear-light
CatmullRom, not SPANF's encoded-space-bicubic training distribution — SPANF
scores are conservative. Rows: benchmarks/sr_pinned_2026-08-03.tsv; analysis
benchmarks/sr_pinned_2026-08-03.md.
Superseded 2026-08-03: the previous table reported the difference of per-method medians, which produced a "textures −1.7 dB / −1.7 SSIM2 (SR loses on stochastic detail)" entry. Paired on that same data, textures is +8.79 SSIM2 with SR winning 7 of 8 files; the −1.7 compared two different images. Per-file values are unchanged and reproduce exactly — only the statistic did.
just export # NTIRE25 ckpts -> models/*.onnx (needs torch; box GPU used by SPANF init)
just dump # SPANF weights + golden -> models/*.raw
just bench # tract grid -> benchmarks/
just verify # golden-gate zensr-micro + timing
just size # build + report micro-abi cdylib sizeThe decisive fix was the overlapped final vector tile: scalar edge columns had covered 25% of width at 64² (each doing the full cin×ky reduction). With edges reduced to 2 columns, the interleaved paired shootout (benchmarks/shootout_2026-07-22.txt, load ~4.4, min-of-14):
| size | tract 0.23.4 | micro-v4x | ratio |
|---|---|---|---|
| 64²→256² | 16.2 ms (4.04 MP/s) | 18.0 ms (3.64) | 1.11× slower (per-call Packed::build) |
| 128²→512² | 71.5 ms (3.67 MP/s) | 57.6 ms (4.55) | 1.24× faster |
| 256²→1024² | 291.9 ms (3.59 MP/s) | 233.4 ms (4.49) | 1.25× faster |
AVX-512 (v4x, f32x16) vs AVX2 (v3, f32x8) on identical kernel source: 1.33–1.42×.
SpanfModel (packed once, Sync) + per-thread Scratch + spanf_x4_tiled(model, input, h, w, threads, tile /*0=auto(128)*/). Exact 16-px halo (= receptive field: 16 chained 3x3
convs); tiled output matches whole-image at the identical 6.557e-6 vs golden; seam test
covers non-multiple dims at 1+3 threads. std::thread::scope + atomic tile counter + mutex
paste — zero new deps. Sweep (512² in → 2048² out, quiet box, min):
| tile | grid | t=1 | t=4 | t=8 | t=16 | t=24 |
|---|---|---|---|---|---|---|
| 112 | 5×5 | 2.63 MP/s | 9.44 | 15.50 | 17.75 | 18.21 |
| 128 | 4×4 | 3.01 | 9.87 | 15.05 | 17.37 | 16.56 |
| 224 | 3×3 | 2.98 | 7.86 | 9.10 (granularity-starved) | 9.41 | 8.95 |
Small tiles win on cache (per-thread scratch: 26 MB @160-ext vs 66 MB @256-ext) AND granularity. Arbitrary dims verified (2026-07-22): SIMD-vs-scalar matrix down to 1x1 and 1-px strips, tiled edge tiles with 1-px cores, and torch goldens at 7x5 / 1x9 / 17x18 through all three paths (<=2e-6) — any h,w >= 1 is supported and tested.
Fusion + stride-padding post-mortem (2026-07-22): both were tried and REVERTED. The Post
store-fusion caused a 26x regression, mechanism IR-verified: rustc strips alwaysinline from
functions containing #[target_feature] calls (soundness interlock), so inlining into the
arcane happens only in rustc's MIR inliner — whose size caps #[inline(always)] does not fully
override. Fusion's exp polynomials pushed conv3x3's MIR over the cap → rejected → emitted
featureless ({ nonlazybind uwtable "target-cpu"="x86-64" }, no alwaysinline) → LLVM legally
cannot inline +avx512f intrinsics into it → call _mm512_fmadd_ps in the hot loop. Fast
builds have no conv3x3 define in IR at all (fully MIR-inlined). Rule of thumb: keep
arcane-adjacent kernel bodies small; hoist transcendental math into separate per-tier fns. And +16 stride padding at 4-KiB
planes measured 2x slower at 128²/256² — the L1-set-aliasing hypothesis is falsified for
this access pattern. The cs channel-stride plumbing is kept at identity stride. Row-band
tiling was evaluated and rejected (50–377 MB per-thread scratch on wide images — 2D tiles
scale better). Practical: a 1 MP image → 16 MP ×4 output in ~230-240 ms on this 7950X
(vs ~3.7 s single-thread untiled). Note: tract could be externally tiled the same way —
the engine-level single-thread comparison (micro 1.25× faster) is the apples-to-apples one.
- Fuse SiLU/gate into conv stores; pixelshuffle straight from the conv2 tile.
- Plane-stride padding (+16 floats) to break L1-set aliasing at power-of-2 sizes.
- cargo-bloat diet toward sub-300 KB full-SIMD; single-tier builds for known fleets.
- Row-band tiling (halo only left/right) for lower halo tax on wide images.
- If int8 compute is ever wanted: QAT via MAI recipe, or switch arch to ECBSR/ETDS class.
Status honestly stated: correctness (all tiers), size menu, f16 path, and the tract comparison are all measured; 64² still trails tract by 11% (per-call weight packing — fix queued); nothing is product-wired (no zenpipe/imageflow integration, no zencodec surface). Weight licensing: NTIRE2025_ESR repo is MIT but per-team weight terms are unaffirmed — fine for experiments, diligence (or retrain on our corpora) before shipping.
cargo build --release -p zensr-micro -p zensr-zenjpeg # the restoration crates
cargo build --release -p zensr-bench # the measurement harnessTwo dependencies are pinned to git revisions rather than crates.io releases,
because the versions we need are unpublished: zenjpeg 0.9.0 (0.8.4 keeps
DeblockMode crate-private, which the deblock-policy code needs) and
zenanalyze 0.2 (the content-class features the router was trained against).
Both repos are public, so a plain cargo build resolves them. When those
crates publish, the pins become ordinary version requirements.
If you have the sibling zenjpeg checkout, just cargo-local writes a
gitignored .cargo/config.toml that points the dependency at your working
tree instead of the pinned revision.
zensr-bench additionally needs cjpeg (libjpeg-turbo) on PATH for the
encode arms, and the imazen-26 corpus, which is not distributed here.
AGPL-3.0-only OR LicenseRef-Imazen-Commercial — the same dual license as the
rest of the zen stack (zenpng, zenresize, zensim). See LICENSE-AGPL3 and
LICENSE-COMMERCIAL.
Nothing here is released. The measured record lives in SYSTEMS.md, the
forward plan and the falsification registry in ROADMAP.md, and the proposed
public surface in docs/API_DESIGN.md.
Numbers predating 2026-07-31 were measured against a partly JPEG-sourced eval
split, and on 2026-08-02 the file selection was found to admit training images
as well. The tier table and the SR table above have both been re-measured on
clean references with pinned selection (2026-08-03) and are current. Anything
else in this file dated earlier has not been re-measured — treat those
figures as provisional and check ROADMAP.md §0 before quoting them.
Two eval harness defects caused this and are now fixed at the source: file
selection is pinned to the held-out split (now
eval_split/imazen26_effective_split.tsv) by both eval
binaries, and every row records gt_src so reference provenance can be audited
after the fact. The shared helpers live in zensr_bench; any new eval binary
must use them.