nx_batchscale_kat.nx
buildroot/runtime/nx_batchscale_kat.nx
about
nx_batchscale_kat.nx -- IS THE MATMUL PATH AMORTISING ACROSS m? The decisive perf question, answered
WITHOUT loading a model, so it fits inside a bounded verifier run.
WHY (2026-07-31): the sovereign embedder measures ~350-400ms/token against a 54.5ms roofline
(491MB gguf model / 9011 MB/s measured bandwidth) = **~7x off floor** (ids 1785475195, 1785475543).
The prime suspect is named by the codebase itself: nx_embed_bench exists to ask 'is prefill actually
BATCHING?' and states the rule -- FLAT ms_per_token across m => prefill is NOT batching (wire the m>1
packed path in lazy_matmul); FALLING => batching works and the cost lives elsewhere.
BUT nx_embed_bench CANNOT BE RUN: /api/gate_run executes VERIFIERS ONLY (correct never-brick bound) and
its 491MB model load alone is 16554..20767ms against a 12000ms deadline (id 1785475610). So the decisive
measurement was unreachable.
THIS KAT REMOVES THE MODEL FROM THE QUESTION. Batching is a property of the MATMUL PATH, not of the
weights: if C[m,n] = A[m,k] . W[k,n] costs the same per-row at m=8 as at m=1, then the weight bytes are
being re-read per row and nothing is amortising -- which is exactly what a bandwidth-bound decode looks
like when the m>1 path is unwired. Synthetic W at the real FFN inner dim (k=896, Qwen W_gate) makes the
shape honest while keeping the run bounded.
READING THE RESULT:
us_per_row FLAT across m -> NOT amortising; each row re-streams W. Wire the m>1 packed path.
us_per_row FALLING with m -> amortising; W is read once per tile and the 7x lives elsewhere.
The ratio us_per_row(m=1) / us_per_row(m=8) IS the batching speedup, measured on this host.
HONESTY: this measures the MATMUL path only. It does NOT prove end-to-end prefill batching (attention,
rope and norms are excluded) -- it isolates the one layer the ecosystem's own note points at, and a FLAT
result here is sufficient to act on because no amount of downstream work can amortise what this layer
already re-read. Timings are one run on a shared NAS: ONE WINDOW IS NOT A RATE, so the KAT prints every
m and lets the reader see the shape rather than trusting a single ratio.
license_tier: ORIGINAL No hw writes (Rule 26). expect_exit: 0
dependencies 4 imports · 0 importers
imports: nx_f32_lazy_weight.nxnx_f32_matmul_t.nxnx_f32_cvt.nxnx_fmt.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 36 | const BK: i64 = 896 // real Qwen FFN inner dim |
| 37 | const BN: i64 = 1024 // reduced from 4864 so the sweep fits a bounded verifier run |
| 38 | const BMAX: i64 = 8 |
functions
| 40 | func bs_lcg(s: i64) -> i64 called by 1: bs_fill |
| 46 | func bs_fill(p: *i64, count: i64, seed: i64) -> i64 |
| 57 | func bs_nl() -> i64 { fmt_puts("\n" as *u8); return 0 } |
| 59 | func main() -> i64 |