gem/docs/research/engine-research-record.md

2563 lines
196 KiB
Markdown

# Research record
## Verified model facts
On 2026-08-23, the official Hugging Face configs and ggml-org GGUF headers were checked directly:
- 12B: 48 layers, width 3840, 16 query heads, 8 SWA KV heads, 1 full KV head, FFN 15360.
- 31B: 60 layers, width 5376, 32 query heads, 16 SWA KV heads, 4 full KV heads, FFN 21504.
- Both use a repeating five sliding-attention/one full-attention pattern, 1024 SWA, 262144 maximum
context, 256-dimensional SWA heads, and 512-dimensional full-attention heads.
- Dense 12B/31B have no cross-layer shared KV. Full-attention layers reuse the K projection as the
V input and omit `attn_v.weight` at every sixth layer, but the cache rows diverge after learned K
normalization versus unweighted V RMS normalization and cannot be aliased directly.
- The normalization dataflow nevertheless exposes a future lossless compact-cache candidate:
store the unweighted normalized V row once and reconstruct historical K as `V * key_norm` inside
full-attention kernels. This must first pass an operator speed/differential gate and then cover
short prefill, long prefill, decode, verifier, and rollback paths before changing cache layout.
- Sparse files constructed from the first 64 MiB of the published Q4_0 artifacts validated the
authentic directory without downloading weight data. The loader accepted 667 tensors for 12B
and 833 for 31B. Published Q4_0 layout consists of Q4_0 matrices, Q8_0 token embeddings, and F32
norms/scales.
- The native raw-UTF-8 BPE tokenizer matched the pinned `llama-tokenize` oracle on the authentic
12B vocabulary for ASCII, Unicode, whitespace/newline runs, byte fallback, and canonical
Gemma 4 chat/control-token prompts.
- The authentic 31B Q4_0 tensor directory bound directly from mmap into 60 fixed native layer
descriptions. The first custom Metal Q4_0 SIMD-group matvec matched the scalar C result locally;
this is correctness evidence, not yet a throughput result.
- The native model-level path now owns Q8_0 embedding/logit projection, all 60 Metal layers, F32 KV,
sampling, detokenization, canonical Gemma 4 chat/tool formatting, and OpenAI HTTP/SSE transport.
A real `../hax/build/hax` process passes discovery, `/props`, request, streaming, usage, and final
output against a fixture backend using this exact server implementation. Authentic-weight token
generation and tool-call quality remain open until the full 31B artifact is available locally.
- The official 2026-07-09 Gemma 4 canonical template uses `<|turn>`/`<turn|>` roles, dedicated
thinking-channel tokens, `<|tool>` declarations, `<|tool_call>`/`<|tool_response>` handshakes,
and `<|\"|>` string delimiters. The native formatter implements that structured subset directly;
it does not embed a general Jinja runtime.
## Native Metal diagnostic: buffer ownership
On the 40-core M4 Max, the native Q4_0 SIMD kernel was measured at the actual 31B FFN projection
shape (5376 input, 21504 output, 65,028,096 matrix bytes). The only changed axis was buffer mode.
Each raw sample contains 200 synchronized dispatches:
- copy-per-dispatch: 13.35 GB/s mean, 1.36% coefficient of variation;
- persistent/no-copy: 107.43 GB/s mean, 4.19% coefficient of variation;
- delta: +704.97%, above the 4.19% observed noise floor.
The evidence is stored in `artifacts/2026-08-23-q4-matvec`. This validates persistent buffers as an
architectural requirement, but `promotion.eligible` remains false because the deterministic
real-model generation gate and model-level `gem print-config` do not exist yet. The persistent
result is also far enough below expected platform memory bandwidth to justify profiling SIMD row
geometry and command submission before drawing a decode-throughput conclusion.
A second one-axis diagnostic held persistent buffers and the 31B FFN shape fixed while changing
command-buffer span from one dispatch to seven. Three 1,000-dispatch repetitions measured 99.65
GB/s versus 138.61 GB/s, a +39.10% delta above the 2.75% noise floor. Evidence is under
`artifacts/2026-08-23-command-span`; promotion remains withheld for the same model-level gates.
The native Metal correctness path now includes weighted RMSNorm over one or many heads and
NeoX/proportional RoPE with optional frequency factors. A dependent RMSNorm-to-Q4 projection and a
two-matvec chain execute in one command buffer and match the scalar C oracle.
The native scheduler's token-span path passed the authentic 31B Q4_0 gate. Against layer span it
improved pp from 7.100 to 7.561 t/s (+6.48%) and tg from 6.946 to 7.427 t/s (+6.93%) over three
repetitions, above 1.35%/1.23% noise floors, with bit-identical native logits. The analyzer reported
the single `command_span` axis comparable and promotion eligible, so token span is the default.
Raw evidence and configuration records are in `artifacts/2026-08-23-native-command-span-31b`.
For prompt `Hello` (tokens `[2, 9259]`), the native layer scheduler and pinned llama.cpp oracle both
selected token 100 and had the same top-10 ordering except for one adjacent swap. Across all 262144
logits, correlation was 0.999852, NMSE 3.03e-4, mean absolute error 0.0391, and maximum absolute
error 0.3312. The comparison used the exact same 31B Q4_0 file (SHA-256
`031dc1c5fa9c5a0abbf3c39c5173fb2af65f5ac2dc2a090268561d3c72dcd834`).
The first real-model roofline showed the one-row Q4_0 kernel at roughly the same 139 GB/s as its
microbenchmark, accounting for native tg 7.43 t/s versus llama.cpp 21.56 t/s. After the pinned
reference MTL0 MUL_MAT backend suite passed all 1154 cases, the native kernel independently adapted
its four-output-row SIMD geometry. The real-shape microbenchmark reached 331.93 GB/s (+139.47%);
model pp reached 19.80 t/s (+161.82%) and tg reached 18.99 t/s (+155.65%). The native logits kept
the same argmax and top-10 with NMSE 2.60e-11 versus the prior kernel. A follow-up two-SIMD-group
threadgroup changed only that scheduling axis and failed promotion (+1.04% tg inside 3.52% noise,
-1.67% pp), so the one-SIMD-group/four-row geometry remains selected. Evidence is under
`artifacts/2026-08-23-q4-multirow`.
The server sampler previously sorted all 262144 vocabulary entries for every non-greedy token,
even with the default `top_k=40`. A fixed-capacity worst-first heap now selects only the requested
top-k set in O(V log k), followed by the same descending sort and top-p sampling over those k
entries. The exhaustive `top_k=0` path is unchanged. Three 20-iteration samples fell from
26.525 ms/token to 0.959 ms/token (27.66x, -96.38% latency; 0.11% CV). A 128-sample seeded test
matches the full-sort token sequence, and equal-logit selection is deterministic by token ID.
Evidence is under `artifacts/2026-08-23-sampler-topk`.
The first batched-Q4 experiment accumulated four input rows with 16 scalar accumulators per SIMD
lane. It regressed the 31B FFN-shape diagnostic from 5,694 to 2,291 rows/s (-59.76%) and was
rejected. Its replacement independently specializes the pinned oracle's legacy Apple
`simdgroup_matrix` strategy: each 128-thread group dequantizes one 64x32 Q4 weight tile to half in
threadgroup memory and reuses it across as many as 32 prompt rows, with float accumulation. The
native five-row CPU differential passes. At 32 rows the diagnostic measured 26,796 rows/s versus
4,385 at one row (6.11x, +511.11%); the reported 1.74 TB/s is equivalent weight bandwidth from
reuse, not physical DRAM bandwidth. The crossover is eight rows, so smaller batches retain the
four-output-row matvec kernel. The fixed graph now uses this operator for prompt tiles up to 32
rows. On a 19-token authentic 31B prompt, tiled versus scalar-in-time logits retained the same
argmax and top-10 set, correlation 0.9999989, and NMSE 3.17e-7. A one-axis token-span comparison
(`n_ubatch` 1 versus 32) improved pp from 19.65 to 23.60 t/s (+20.13%) over three repetitions; tg
changed +1.49%, within the 2.05% candidate noise floor. The analyzer marked promotion eligible, so
batch 32 is the production default. Raw model evidence is in
`artifacts/2026-08-23-native-prefill-31b`; operator records and configuration are in
`artifacts/2026-08-23-prefill-matmul4`.
The original decode-attention kernel assigned one 32-lane SIMD group per query head, scanned every
token serially, and redundantly evaluated softmax work in every lane. At the 31B full-attention
shape (32 Q heads, 4 KV heads, width 512), throughput collapsed from 100.11 dispatches/s at 1K
tokens to 2.784 at 32K, only about 12 GB/s of logical KV traffic. After the pinned reference's
complete MTL0 `FLASH_ATTN_EXT` suite passed 4792/4792 cases, the native kernel independently
adopted token-parallel blocked scheduling: 256 threads distribute score dot-products, reduce
max/sum in threadgroup memory, then distribute value dimensions. It switches at eight live tokens,
the measured crossover. At 32K it reaches 35.767 dispatches/s (12.85x, +1184.74%) and 153.6
logical GB/s. Authentic 16-token 31B logits retain the same argmax and top ten, with correlation
0.9999992 and NMSE 1.41e-7. At 256 prompt tokens, model pp rises from 25.91 to 58.24 t/s
(+124.78%); tg changes +2.24% inside noise. The one-axis analyzer marks it promotion eligible, so
token-parallel attention is the production default.
An F16 KV candidate halves cache memory and raises the 32K attention diagnostic another 8.25%.
Authentic logits retain argmax/top-ten with NMSE 2.11e-7, but a clean 256-token model comparison
changed pp only +0.77% and tg +0.12%, both inside variance. It therefore remains unpromoted and the
default cache stays F32 until a longer model-level gate justifies the representation change. Raw
evidence is in `artifacts/2026-08-24-attention-kv`.
A follow-up GQA-reuse experiment split attention into 256-token online-softmax tiles and reused each
K/V tile across the query heads sharing a KV head. The operator result was attractive: versus the
promoted parallel kernel, the 31B full-attention shape improved 1.81x at 2K, 2.81x at 8K, and 3.57x
at 32K; the 1K SWA shape improved 1.99x. The authentic gate rejected it. At 1,024 prompt tokens the
31B logits still matched argmax and top-10 order, but NMSE had already accumulated to 4.20e-3 and
mean absolute error to 0.326. The first full-attention GQA8 dispatch at 1,792 tokens produced all
NaN logits, and a SWA-only GQA2 restriction also produced all NaNs by 2,048 tokens. Normalizing
tile partials worsened the 1,024-token NMSE to 1.18e-2. A second design retained one global softmax
by separating GQA score generation from value accumulation, but extra dispatch/scratch traffic
erased the isolated speed advantage. All candidate kernels and runtime switches were removed; the
production path remains the previously promoted per-query-head parallel kernel. Raw measurements,
failed logits, and the rejection record are in `artifacts/2026-08-24-blocked-attention`.
This failure tightens the next attention gate: a candidate must compare repeated wrapped-ring
operator outputs at the exact 31B SWA/full shapes, then pass at least a 2K authentic model-logit
differential before performance timing. A small synthetic operator comparison is insufficient for
an online-softmax decomposition because layer-to-layer numerical error can compound catastrophically.
The verified token-parallel kernel was then profiled directly with Metal command-buffer GPU
timestamps because this Command Line Tools installation has no `xctrace`, and the M4 Max exposes
only the `GPUTimestamp` counter set with stage-boundary sampling. At 1K full attention, changing
command span from one to twenty raised throughput from 1,125.78 to 1,393.11 dispatches/s and GPU
wall fraction from 79.36% to 98.52%; short-context submission overhead is therefore material. At
32K, span one was already 99.25% GPU time and span five reached 99.84%, while throughput changed
only 0.91%. Long-context optimization must reduce kernel work or memory traffic rather than launch
count. F16 KV reached 38.23 dispatches/s versus 35.67 for F32 in this operator profile, consistent
with the earlier capacity result but still not a model-level promotion.
An exact-softmax follow-up staged normalized score weights once per 256-token value tile in
threadgroup memory. It passed the native CPU differential, and the reference MTL0
`FLASH_ATTN_EXT` suite passed 4,792/4,792 before the local gate. A longer fresh 32K comparison
measured only +1.00% wall and +0.98% GPU throughput, close to the 0.87% baseline coefficient of
variation and below the 3% promotion floor. The candidate was removed; raw profiles and the
rejection record are in `artifacts/2026-08-24-attention-profile`.
The successful follow-up preserved the same global score, maximum, denominator, and normalized
weights, but changed the larger traffic term: value accumulation dispatches one workgroup per
KV-head, eight-query-head block, and 64 output dimensions. Each workgroup loads a 32-token x
64-dimension V tile once and shares it with the applicable GQA heads. This avoids online-softmax
state merging entirely. A post-promotion stress audit found that both this score pass and the old
parallel kernel lacked a phase barrier: a fast thread could overwrite the reduced maximum in
`shared[0]` with a denominator partial before every thread consumed it. Adding the barrier to both
kernels made 100 independent test processes deterministic, with two identical invocations of each
kernel at every exact 12B/31B full/SWA wrapped-ring shape.
All gates were rerun after the fix. Against the corrected per-query-head kernel, full 31B attention
improves 2.38x at 1K and 2.67x at 32K; the 1K SWA shape improves 1.74x. At 2,048 authentic 31B
prompt tokens, all 262,144 logits remain finite, argmax and the top-10 set match, correlation is
0.999793, and NMSE is 4.11e-4. The three-repetition model gate raises pp from 23.97 to 31.95 t/s
(+33.29%, above the 12.18% noise floor). The measured tg delta is -10.60% inside a 20.31% noise
floor and therefore is not a detected regression. Normal and ASan/UBSan suites pass 22/22, the
production binary remains free of llama/ggml linkage, and `gem agent` returned exactly `OK` through
real hax at a 4K context with port cleanup verified. Corrected raw evidence and the promotion record
are in `artifacts/2026-08-24-split-gqa-race-fix`; the earlier record is marked superseded.
The local llama.cpp checkout remains useful for its Gemma 4 graph, converter, tokenizer behavior,
Metal kernels, and DSV4 optimization techniques. Its DSV4 operators are not emitted by the Gemma 4
graph and are reference material, not a linked dependency or automatic speedup.
After split-GQA promotion, prefill projection became the next target. A Q4 `64x64` prototype used
eight SIMD groups so each dequantized 64-output by 32-K weight tile served 64 prompt rows. It passed
a 65-row CPU differential including the tail and the reference MTL0 `MUL_MAT` suite passed
1,154/1,154. The larger tile nevertheless regressed the 31B FFN shape by 7.23% at 64 rows and
30.03% at the production 32-row batch. Extra SIMD/register pressure outweighed weight reuse, so
the candidate and runtime API were removed. Raw evidence is in
`artifacts/2026-08-24-q4-prefill-64x64`.
The next attention pass targeted the remaining K traffic. The promoted split-GQA score kernel had
still loaded each K vector once per query head. A cooperative score pass now stages K once per
KV-head, eight-query-head block, and 32-token chunk; eight SIMD groups compute the associated query
heads, followed by a separate exact global normalization pass and the existing shared-V pass. This
keeps the same global softmax and supports the exact 12B/31B full/SWA ratios.
The reference MTL0 `FLASH_ATTN_EXT` suite passed 4,792/4,792, and 100 independent wrapped-ring
stress processes were deterministic. At 1K/32K full attention the operator improves 27.41%/49.76%;
1K SWA improves 3.09%. Authentic 2K logits are all finite with matching argmax/top-10 set,
correlation 0.999824, and NMSE 4.64e-4. The 2K model result (+4.48% pp) was correctly withheld
because it sat inside a 12.56% noise floor. A stable 4K gate then improved pp from 31.74 to 34.60
t/s (+9.00%, 2.56% noise) while tg improved 0.92%. Normal and sanitizer suites pass 22/22, linkage
is native-only, and real hax returned exactly `OK` with cleanup verified. The cooperative-K path is
now the default; evidence is in `artifacts/2026-08-24-gqa-score-reuse`.
The next prefill pass isolated threadgroup layout while preserving Q4_0 storage, the 64-output by
32-prompt tile, four SIMD groups, F16 staging, and F32 accumulation. A bank-aware 8-wide swizzle,
independently adapted from the pinned MIT llama.cpp legacy `MUL_MM` layout, improves the 31B FFN
projection 44.78% and the down projection 95.02%; every tested authentic projection/batch shape
cleared its variance floor. The reference MTL0 `MUL_MAT` suite passed 1,154/1,154. A 33-row CPU
differential covers the complete MMA tile and tail, and authentic logits are bit-identical across
all 262,144 entries. The promotion gate improves 32-token pp 35.84% with 64-token generation
unchanged; a 512-token confirmation improves pp 26.45%. Normal/sanitizer suites pass 22/22,
linkage remains native-only, and real hax returned exactly `OK` with port cleanup verified. The
linear kernel was removed; evidence is in `artifacts/2026-08-24-q4-prefill-swizzle`.
With swizzled projections promoted, a native profiler was added because `xctrace` is unavailable in
the installed command-line developer tools. Metal command-buffer timestamps show 32/512-token
prefill spending 99.07%/99.33% of wall time on the GPU while issuing 11,732/200,312 dispatches.
Batching RoPE positions into one grid per Q/K tensor removes 3,720 dispatches at 32 tokens and
59,520 at 512 tokens, improving the profiling workloads 5.37%/6.71%. The one-axis authentic gate
improves pp 12.10%, logits are bit-identical, the reference MTL0 `ROPE` suite passes 448/448, and
normal/sanitizer/linkage/hax gates pass. Evidence is in
`artifacts/2026-08-24-prefill-dispatch-profile`.
A follow-up non-wrapping batched-KV prototype removed another 1,860 dispatches at 32 tokens and
29,760 at 512 tokens, while preserving exact logits and wrapped-store operator correctness. Its
first sequential runs appeared 5.68%/6.45% slower, but the post-removal row-wise control drifted
below the candidate, exposing thermal/run-order confounding. The candidate was removed and is
classified as inconclusive rather than a negative result. A retry must interleave both layouts in
one process; row-wise KV storage remains production behavior.
FlashAttention and online-softmax primary sources were revisited through Paperbridge before the next
prompt-attention design. The retained principle is IO-aware fusion without materializing scores in
device memory; the rejected tile-local recurrence was not reused. The new initial-prompt kernel is
bounded to at most 32 non-wrapping causal rows, holds the entire per-row score vector in threadgroup
memory, and performs the same global maximum and denominator reductions as the proven parallel path.
It passes a CPU differential, the pinned MTL0 `FLASH_ATTN_EXT` oracle (4,792/4,792), and a 2K
authentic logit gate with identical argmax/top-10 order (correlation 0.9997926, NMSE 7.51e-4). It
reduces the 32-token model profile from 8,012 to 3,152 dispatches and improves interleaved pp 6.85%
without a tg regression outside variance. The unfused split-GQA route remains selectable as an
explicit differential and benchmark oracle.
Extending the one-dispatch causal kernel beyond the initial tile was rejected after authentic-logit
testing. Although 64/128/512-token profiles initially looked faster, both the first prototype's
incomplete score initialization above 256 tokens and the corrected implementation changed repeated
layer numerics beyond the quality gate. Production therefore retains that kernel only for the first
32 tokens. The accepted later-tile design instead batches the proven rowwise arithmetic: cooperative
GQA score generation, exact global normalization, and shared-V accumulation become three grids over
all rows. The operator result is bit-identical to the rowwise oracle at 308 live tokens. Authentic
2K logits preserve argmax and top-10 order (correlation 0.999835, NMSE 2.54e-4). At 512 tokens,
dispatches fall from 135,932 to 52,232 and the formal interleaved model gate improves pp 16.57%
when comparing the combined prompt path with the fully rowwise oracle. A refined one-axis control
holds the promoted initial tile constant and measures only later-tile batching: pp improves from
71.01 to 86.80 t/s (+22.24%, 6.91% noise floor), while tg changes -1.11% inside variance. The MTL0
`FLASH_ATTN_EXT` suite passes 4,792/4,792; evidence is in
`artifacts/2026-08-24-causal-prefill-tiles`.
The first later-tile promotion deliberately capped batched scores at 1,024 live tokens. Real hax
project prompts were approximately 2.5K tokens, so the ten 31B full-attention layers still returned
to 32 rowwise score/normalize/value sequences per tile while SWA layers stayed batched. Raising only
the non-wrapping ceiling to 4,096 requires a bounded 16 MiB score arena for the 31B head shape; a
naive 262K extension would require roughly 1 GiB and was rejected. No MSL arithmetic changed. A new
1,032-token operator differential is bit-identical to rowwise attention. Sequential 2K/4K profiles
improve 9.30%/9.17%, reducing dispatches by 29,760/89,280. The exact one-axis, five-pair 2K gate
holds the 1K path constant and improves pp from 50.88 to 54.18 t/s (+6.50%, 2.93% noise); tg changes
+0.28% inside variance. Authentic 4K logits are finite with the same argmax and top-10 set
(correlation 0.999379, NMSE 5.34e-4), although two close top-10 alternatives swap order. The MTL0
suite passes 4,792/4,792, normal and ASan suites pass 23/23, and real hax returns exactly `OK` with
launcher cleanup verified. Evidence is in `artifacts/2026-08-24-prefill-split-4096`.
Decode is now close to a weight-bandwidth problem: the 17.99 GB 31B Q4_0 model at roughly 18.8
generated tokens/s implies about 338 GB/s of model-weight traffic, consistent with the native
four-row Q4_0 microbenchmark's 336--344 GB/s controls. A paired half-block SIMD-lane geometry,
independently adapted from the adjacent DeepSeek-V4 llama.cpp reference after its MTL0 `MUL_MAT`
suite passed 1,154/1,154, passed the native CPU differential but reached only 311.43 GB/s. That is
7.29% below the preceding production control and 9.58% below the following control, so the
candidate and its API were removed. Paperbridge review of BaseRT also ruled out directly adopting
its native `base_q4`: it is an asymmetric group-64 format, and the published converter rejects
quant-to-quant conversion. Requantizing GGUF Q4_0 would change model quality and is out of scope;
only a lossless derived layout could be considered. The next decode axis is per-stage GPU timing of
one authentic 31B token before choosing between lossless repacking and projection/epilogue fusion.
Evidence is in `artifacts/2026-08-24-q4-decode-research`.
The corresponding 12B baseline reaches 108.53 pp and 43.21 tg with native F32 KV. The adjacent
llama.cpp default reaches 82.48 pp and 52.68 tg with F16 KV and automatic flash attention; this is
competitor context rather than a one-axis comparison. An authentic position-32 native profile
attributes 32.80% of sampled GPU time to FFN gate/up, 18.42% to FFN down/output, 11.93% to QKV,
8.04% to attention output, and 12.04% to tied logits. A 12B-only revisit of the paired half-block
lane layout was nearly logit-identical to row4, but the formal five-run A/B/A model gate changed
generation from a 41.364 t/s surrounding baseline to 41.144 t/s (-0.53%) with higher variance.
It was removed. Evidence is in `artifacts/2026-08-24-native-12b-baseline` and
`artifacts/2026-08-24-q4-halfblock-12b-model-gate`.
The batch-one Q4 harness now rotates explicit independently backed matrices and reports the total
working set. Sustained 12B gate/up traffic falls from 335.64 GB/s for one resident 31.64 MiB matrix
to 313.75 GB/s for a 253.13 MiB eight-matrix stream (-6.52%). A single 31B gate/up matrix is already
62.02 MiB: expanding to a 248.06 MiB four-matrix stream changes 372.48 to 376.85 GB/s (+1.17%,
inside variance). At the 12B streaming shape, increasing only command span from seven to 336 raises
314.33 to 421.62 GB/s (+34.14%). Production already records an entire token in one command buffer;
future kernel screens must therefore use both a nonresident working set and graph-like span.
Evidence is in `artifacts/2026-08-24-q4-decode-streaming-harness`.
That corrected evidence shifted the next bounded axis to tied Q8 logits, which account for 12.04%
of sampled 12B and 7.34% of sampled 31B decode GPU time. A four-output-row Q8 matvec passed the
native CPU differential and the immediately preceding adjacent MTL0 MUL_MAT oracle passed
1,154/1,154. It nevertheless regressed the clean 12B exact-shape operator from a 329.18 rows/s
surrounding baseline to 295.50 rows/s (-10.23%). The 31B long run entered thermal collapse; even
its optimistic early samples implied only a single-digit stage gain, whose end-to-end ceiling is
below 0.5%. The diagnostic kernel, API, test, and switch were removed before model integration.
Evidence is in `artifacts/2026-08-24-q8-row4-decode`.
M4 Max exposes the timestamp counter set at compute-stage boundaries but not at arbitrary dispatch
boundaries. A diagnostic-only profiler therefore keeps one command buffer and places 542 sampled
compute passes around embedding, nine fixed groups in each of 60 layers, and logits. At decode
position 32, three runs cover 97.64% of the command buffer's GPU interval. Projection-dominated
groups account for 81.29% of sampled time, KV store/attention 6.04%, and tied-output logits 7.34%;
explicit norm, transform, and activation groups together are only 5.30%. The five-run production
control reaches 20.27 tg, or 49.34 ms/token. Sample boundaries add diagnostic overhead, but the
profiled route's 262,144 logits are bit-identical to the same ordinary one-token schedule. This rules
out norm/activation fusion as the next end-to-end 3% lever. Evidence is in
`artifacts/2026-08-24-decode-stage-profile`.
The trace justified one lossless derived-layout experiment. Within every Q4_0 row, all binary16
scales were grouped before the nibble payload while preserving the exact 18 bytes per block. The
candidate passed its CPU differential but reached 324.21 GB/s at the 31B FFN shape, versus
371.49/382.49 GB/s surrounding direct-GGUF controls. The sidecar layout and kernel were removed;
direct Q4_0 remains production. Evidence is in `artifacts/2026-08-24-q4-split-layout`.
Paperbridge retrieval of speculative sampling and Draft & Verify identifies the next architectural
lever: draft a short continuation, score it in parallel with the target, and use rejection sampling to
preserve the target distribution. The necessary premise is that short parallel scoring approaches the
latency of one target token. Gem's current 31B FFN projection takes 0.188 ms for one row,
0.627 ms for four rowwise matvecs, and 0.798/0.808 ms for the existing 64x32 MMA route at 8/16
rows. The fixed 32-row tile therefore costs 4.25x a decode projection at eight rows and cannot yet
serve as an efficient verifier. The next bounded kernel axis is a 64-output x 8-token specialization;
only after it clears correctness and latency gates should the engine add draft/accept/rollback state.
The aligned 12B draft GGUF is not currently local, so acceptance-rate measurement remains a later
gate. Evidence is in `artifacts/2026-08-24-speculative-feasibility`.
The fixed 64-output x 8-token Q4_0 verifier specialization passes the adjacent reference backend's
1,154/1,154 MUL_MAT cases and Gem's native CPU differential. It uses two SIMD groups and 4.5 KiB
of threadgroup memory instead of four groups and 8 KiB, while preserving the direct GGUF layout,
binary16 staging, and float accumulation. At the representative 31B FFN projection it lowers an
eight-row call from 0.799/0.800 ms in surrounding 64x32 controls to 0.500 ms (+59.76% row
throughput), and is 2.45x faster than eight rowwise matvecs. Authentic 31B logits after an eight-token
prefill are bit-identical across all 262,144 vocabulary entries. The five-run full-model gate improves
pp from 29.50 to 31.17 t/s (+5.65%, 0.073% noise floor); tg changes +0.21% inside variance, and a
following old-kernel control returns to 29.48 pp. Production therefore selects 64x8 only at exactly
eight rows, retaining matvec below eight and 64x32 above it. Normal and ASan suites pass 24/24.
This clears the dense-projection premise for short verification, but not speculative decoding as a
whole: batched tied-output Q8_0 logits, an authentic verifier profile, draft acceptance, and KV
rollback remain gated work. Evidence is in `artifacts/2026-08-24-q4-verify8`.
The tied Q8_0 output projection now has a matching fixed-eight-row SIMD-matrix path. At the real
5,376 x 262,144 embedding shape it computes all eight vocabulary distributions in 4.96 ms versus
30.43 ms for eight rowwise matvecs. In the authentic 31B graph, changing only the final Q8 schedule
reduces the complete eight-token verifier pass from 291.85 to 265.39 ms (-9.06%, five runs) and
removes seven dispatches. All 2,097,152 logits are finite; relative to the rowwise target they retain
the same global top-10 order with correlation 0.999999936, NMSE 1.92e-8, MAE 0.00112, and maximum
absolute error 0.00952. The small difference is expected from binary16 SIMD operand staging versus
the float matvec path. This kernel remains scoped to the explicit verifier API; ordinary one-token
logits retain the Q8 matvec. Normal and ASan suites pass 24/24. Evidence is in
`artifacts/2026-08-24-q8-verify8`.
Doubling the fixed Q4_0 64x8 tile from two to four SIMD groups splits the same 64 outputs across
four 16-column slices. The direct GGUF layout, binary16 operand staging, float accumulation, and
4.5 KiB threadgroup allocation do not change. At the 31B FFN shape, four groups lower the call from
0.514 to 0.426 ms (+20.75% row throughput). Across the authentic target graph, with batched Q8 held
constant, the effect compounds: eight-token verification falls from 265.63 to 166.22 ms (-37.42%,
five paired runs), and all 2,097,152 logits are bit-identical. The normal five-run model gate improves
eight-token pp from 31.24 to 51.42 t/s (+64.61%, 0.324% noise floor), while tg changes -0.21% inside
variance; a following two-group baseline returns to 31.20 pp. Production now selects four groups at
exactly eight rows only. Normal and ASan suites pass 24/24. This makes target verification about
3.4x the current one-token decode latency before draft cost, so speculative decoding has a plausible
but acceptance-sensitive ceiling; the next empirical gate is a local aligned 12B draft. Evidence is
in `artifacts/2026-08-24-q4-verify8-4sg`.
The official 12B Q4_0 GGUF is now locally available and hash-verified (7,219,673,216 bytes,
SHA-256 `3712b9bd32cae83a22f67ee7a4466d8d7a4f21646ac8a07d19bf9418e8767a70`); it is
accepted by the strict native 48-layer profile and is excluded from YAMS indexing. Native 12B
decode reaches 44.92 t/s, or 22.26 ms/token. Eight draft tokens plus the promoted 31B verifier cost
about 344.29 ms, so break-even against 49.08 ms direct target decode requires 7.02 emitted tokens
per round, equivalently just over six accepted drafts on average. A new diagnostic replays shared
prefixes and measures greedy 12B/31B agreement over four agent-style prompts and 64 K=8 rounds.
Mean accepted prefixes are 0.375--1.188 for general prompts and 3.625 for a repetitive JSON prompt;
none reaches the 6.015 break-even requirement. Even the best case estimates 74.44 ms/token before
scheduler overhead, 51.7% slower than direct target decode. A greedy dual-model scheduler is therefore
rejected. This does not yet reject stochastic speculative sampling: the next bounded gate is exact
temperature-0.8/top-k-40/top-p-0.95 distribution overlap. If that fails, Gemma 4's small MTP artifact
or self-speculation is structurally more promising than paying for eight full 12B draft passes.
Evidence is in `artifacts/2026-08-24-speculative-12b-gate`.
Exact stochastic filtering confirms the full 12B draft is not viable: across three seeds and four
agent-style prompts, its mean emitted tokens per K=8 round remains below the 7.015 break-even point.
The official Gemma 4 assistant sidecars are structurally different. Their four 1,024-wide layers
reuse the target's final SWA/full KV views, and the matching 31B Q4_0 artifact is only about 267 MiB
(SHA-256 `f8f1b8c89b72da2b7391d6fe29d26dd373efe9624a3d5b3f0e99e62579b12c08`).
Gem now validates both the 3,840-wide 12B and 5,376-wide 31B sidecar contracts and executes the
assistant natively: target-sampled token plus post-final-norm hidden, 10,752-to-1,024 pre-projection,
three shared-SWA query-only blocks, one shared-full query-only block, tied Q4_0 output, and recurrent
1,024-to-5,376 post-projection. Seven warm 31B assistant steps cost 32.75--34.37 ms total.
The native server now performs exact stochastic MTP rounds: one guaranteed target sample, seven
assistant proposals, one fixed-eight target verifier, acceptance probability `min(1, q/p)`, residual
sampling from normalized `(q-p)+`, and a target bonus sample when all proposals pass. A deterministic
streamed 64-token request transfers the same 3,255 bytes with and without MTP. After one discarded
warm-up, three direct requests average 1.555971 s and three MTP requests average 1.478071 s, a
5.01% latency reduction / 1.053x speedup. The improvement exceeds both 3% and the larger 1.19%
coefficient of variation. MTP is therefore promoted for `gem serve/agent --mtp`. Before each
verifier the engine snapshots only SWA ring slots that would overwrite live entries; rejection
restores the unaccepted tail before the correction token, so MTP remains correct past position
1,024 without copying the full cache. Evidence and raw JSON are in
`artifacts/2026-08-24-native-mtp-server`.
An M4 Max stage trace then isolated the MTP drafter itself. Across three seven-token runs, the four
assistant layers, pre-projection, tied output, and post-projection consumed 6.87--6.92 ms of sampled
GPU time, while wall time remained 32.92--33.97 ms. A detailed run spent 30.69 ms in command
submission/wait versus 0.52 ms encoding and 0.11 ms logits readback, so assistant matmul tuning was
not the causal next axis. PaperBridge retrieved Wagner et al., *Optimized Speculative Sampling for
GPU Hardware Accelerators* (arXiv:2406.11016), whose exact method likewise keeps probability work
parallel and writes back only acceptance-relevant results. Gem does not adopt the paper's inexact
sigmoid alternative.
The promoted response keeps seven recurrent drafts in one Metal command buffer. Its fixed top-k40
block sort/merge is independently adapted from the pinned MIT llama.cpp Metal TOP_K implementation;
the M4 Max backend-op oracle passed 445/445 cases before adaptation. A local 262,144-token CPU
differential and indirect Q8 embedding differential pass. Device nucleus draws export the same
sparse probabilities used to generate each proposal, preserving exact speculative acceptance;
unsupported top-k or repetition-penalty settings retain the CPU path. With one discarded warm-up
and three interleaved repetitions, identical greedy proposals improve from 34.262 to 31.764 ms for
seven drafts (-7.29%, 1.079x). The accompanying deterministic HTTP/SSE gate averages 1.793911 s
direct and 1.701882 s device-MTP (-5.13%, 1.054x), with identical HTTP status and byte counts.
Evidence is in `artifacts/2026-08-24-mtp-device-chain`.
The production-path stochastic gate now reports phase totals from the same device-resident draft
chain used by the server. Across three identical five-round runs, seven-token drafting costs
12.46--12.69 ms per round and exact CPU target-distribution/acceptance work costs only
1.87--1.88 ms per round. Fixed-eight target verification instead costs 159.21--159.61 ms per
round, of which 99.5% is measured GPU time; rejection/bonus reconciliation costs another
40.49--40.92 ms per round. A new 542-stage counter trace attributes 94.40% of sampled verifier GPU
time to Q4 projections: 38.38 ms QKV, 21.10 ms attention output, 50.59 ms FFN gate/up, and
48.81 ms FFN down. Attention itself is 1.34 ms and tied logits are 4.90 ms. The profiled and
ordinary verifier paths produce bit-identical 2,097,152-float output with SHA-256
`72c9300b97c46705f24d4880cd1de7fde150dbe6183bc463aeac9c99d74d42d1`.
The adjacent M4 Max Metal MUL_MAT oracle passed 1,154/1,154 cases before a bounded fixed-eight
4-to-8-SIMD-group experiment. The candidate reaches 17,028.65 rows/s against 18,659.37 rows/s
across stable surrounding four-group controls, an 8.74% regression, so it was removed. More
thread-level partitioning is therefore not the next lever; the four-group kernel remains
production. Evidence is in `artifacts/2026-08-24-verifier-stage-profile`.
Two follow-up fixed-eight Q4 schedules were also rejected. A 128-output x 8-token tile retained
four SIMD groups and halved threadgroup count, but doubled scratch from 4.5 to 8.5 KiB and doubled
accumulators per group. It passes the native CPU differential and the immediately preceding
1,154/1,154 reference MUL_MAT gate, but regresses stable row throughput 9.84%, so it was removed.
A second candidate made one thread load each 18-byte Q4 block and expand both nibbles instead of
having two nibble threads reread the block. Its repeated-65-MiB operator benchmark improves about
11.3%, but the authentic 31B verifier improves only 0.30% across five candidate and ten surrounding
baseline passes, inside variance and far below the 3% gate. It too was removed. This discrepancy
shows that repeated-matrix microbenchmarks can overstate cache-sensitive load improvements for the
full model's unique layer-weight stream. Evidence is in `artifacts/2026-08-24-q4-verify8-128x8`
and `artifacts/2026-08-24-q4-verify8-singleload`.
The prefill operator harness now accepts `--matrix-slots` and rotates each dispatch across distinct
GGUF-layout matrices. It reports per-matrix and total working-set bytes, and its bandwidth metric
now counts one matrix read per fixed-eight dispatch rather than multiplying traffic by eight output
rows. A 31B FFN-shape characterization compared a repeated 65 MiB matrix against a four-slot
260 MiB stream. The repeated controls settled near 18.85k rows/s (about 153 GB/s); the rotating run
initially matched that rate, then fell to 13.45k/12.91k rows/s, yielding excessive variance. This
confirms that the mode exposes resident-working-set effects, but it does not support the narrower
claim that cache reuse caused the single-load candidate's failure: stable portions of both modes
were effectively equal. The remaining fidelity gap is the verifier's mixed projection dimensions
and graph schedule. Raw characterization is in
`artifacts/2026-08-24-q4-streaming-harness`.
A four-group K-tile experiment then staged four contiguous Q4_0 blocks (K=128) per barrier
interval instead of one. Each thread expanded two complete blocks, eliminating duplicate nibble
loads and reducing synchronization by four, while preserving the 64-output/eight-token geometry and
GGUF bytes. The native CPU differential covered a 67-output tail, and the adjacent Metal MUL_MAT
oracle passed 1,154/1,154 cases. The candidate nevertheless fell to 14,296 rows/s versus 18,983
rows/s across surrounding controls (-24.69%). Its 18 KiB threadgroup allocation, four times the
production kernel's 4.5 KiB, costs more occupancy than the saved loads and barriers repay on M4 Max.
It was removed before the authentic verifier gate; evidence is in
`artifacts/2026-08-24-q4-verify8-k128`.
PaperBridge research after this fourth rejected schedule found two useful boundaries. Apple's 2026
Metal Performance Primitives guide recommends K=128 only as an M5 starting point and explicitly
warns that barrier frequency trades instruction availability against cache working set; it is not
evidence for M4. RIGEL's M4 Max characterization reports that Metal 4.1 tensor operations remain on
the shader/simdgroup-matrix path and that FP8 is emulated, so a low-precision tensor-format detour is
not a viable M4 optimization. BaseRT independently reports that its native runtime relies on dedicated
GEMV/GEMM kernels and fusion, but its engine and kernels are proprietary and its `.base` weight
format is outside Gem's GGUF ownership boundary. The actionable open-source direction is therefore
a bounded register-resident Q4 fixed-eight kernel, independently adapting llama.cpp's MIT-licensed
register dequantization pattern while keeping Gem's graph and storage unchanged.
That register-resident screen passed an eight-row CPU differential including a 67-output tail and
the immediately preceding 1,154/1,154 oracle. One 64-thread group read each output row's Q4 block
once and kept eight token sums in registers, with no threadgroup scratch or barriers. It reached only
10,886.60 rows/s against 18,828.53 rows/s surrounding MMA controls (-42.18%) and was removed.
The simdgroup matrix path is therefore essential at verifier batch eight: relying on cache for the
64-fold repeated activation loads and replacing matrix instructions with scalar FMAs is much worse.
Evidence is in `artifacts/2026-08-24-q4-verify8-register`.
The production-width specialization was also screened and removed. All accepted Gemma 4 projection
outputs are multiples of 64, so the candidate deleted the per-block output-tail branch and the
scratch-mediated tail store from the four-group MMA kernel while rejecting unaligned widths at the
C boundary. The immediately preceding reference MUL_MAT oracle passed 1,154/1,154 cases and the
aligned native CPU differential passed. The candidate reached 19,068.84 rows/s versus 18,805.56
rows/s across surrounding controls, only +1.40%; its own five-run standard deviation was 364.43
rows/s. This is below the 3% gate and inside variance, so the generic tail-safe production kernel was
restored. Further schedule changes should be driven by compiler instruction and occupancy evidence.
Raw evidence is in `artifacts/2026-08-24-q4-verify8-aligned`.
The next bounded loader change succeeded. The production four-group verifier now reads the sixteen
Q4 payload bytes as eight aligned `ushort` words per nibble thread, extracts two quantized values
from each word, and retains the exact `half(quant - 8) * scale` operation. The packed-word unpack is
independently adapted from llama.cpp's MIT-licensed `dequantize_q4_0` helper; Gem's fixed-eight
schedule, scratch layout, barriers, and matrix accumulation are unchanged. The immediately preceding
M4 Max oracle passed 1,154/1,154 MUL_MAT cases and the varied-nibble CPU differential passes.
The rotating 260 MiB operator screen improves from 18,882.25 rows/s across surrounding controls to
20,413.84 rows/s (+8.11%, 165.93 GB/s). More importantly, authentic 31B verifier GPU time falls from
166.45 ms across surrounding controls to 158.92 ms (-4.52%, 1.047x) over five candidate and ten
control repetitions. Candidate CV is 0.93% and the larger control CV is 1.48%, so the result clears
both the 3% threshold and variance floor. All 2,097,152 verifier logits are bit-identical.
An earlier packed-word formulation used algebraically equivalent `d1`/`d2`/midpoint half arithmetic
and reached 22,447.49 rows/s, but changed half rounding enough to amplify across 60 layers (NMSE
0.1138 and some per-row argmax changes). It was rejected. This is a useful numerical boundary:
preserve the original half operation sequence even when refactoring the unpack. Evidence for both
variants is in `artifacts/2026-08-24-q4-verify8-u16`.
The same exact packed-word unpack does not transfer to batch-one decode. A separate four-row SIMD
candidate preserved the production float accumulation order but loaded each Q4 payload as eight
aligned `ushort` words instead of sixteen bytes. The varied-nibble CPU differential and immediately
preceding 1,154/1,154 reference oracle passed. At the 31B FFN shape with persistent buffers and
command span seven, it reached 369.32 GB/s versus 367.92 GB/s across surrounding controls (+0.38%)
and was 1.73% slower than the stable post-control. It was removed before the authentic model gate.
Unlike fixed-eight staging, batch-one already distributes contiguous blocks across SIMD lanes, so
paired loads only exchange load instructions for extra shifts. Evidence is in
`artifacts/2026-08-24-q4-decode-u16`.
The transfer to 32-row prefill does succeed. The production 64x32 MMA kernel now stages each Q4
payload through eight aligned `ushort` loads while preserving its scratch layout, barriers,
simdgroup-matrix operations, and exact `half(quant - 8) * scale` sequence. The varied-nibble CPU
differential includes a 33-row tail, and the immediately preceding reference oracle passed all
1,154 MUL_MAT cases.
On the rotating 260 MiB operator screen, the candidate reaches 40,261.50 rows/s versus 38,165.60
rows/s across surrounding controls (+5.49%). Authentic 31B prompt processing rises from 110.89 to
114.40 tokens/s (+3.16%) over three repetitions, while generation changes from 20.95 to 21.01
tokens/s (+0.29%). All 262,144 baseline/candidate logits are bit-identical. The separate diagnostic
kernel and API were removed after promotion, leaving one production implementation. Evidence is in
`artifacts/2026-08-24-q4-prefill-u16`.
The refreshed decode profile motivated a lossless FFN gate/up launch-fusion screen. A diagnostic
kernel read the two original mapped Q4 matrices in one launch, kept independent accumulators, and
preserved every output's float operation order. The native paired CPU differential passed, the
immediately preceding MTL0 MUL_MAT oracle passed 1,154/1,154 cases, and authentic batch-one logits
were bit-identical.
The isolated two-matrix screen improved from 384.83 to 408.96 GB/s (+6.27%), but authentic 31B
generation regressed from 19.61 to 18.56 tokens/s across surrounding controls (-5.33%). Combining
the work doubles per-thread accumulators and halves independent threadgroups, so launch savings do
not repay the graph-level occupancy/scheduling loss. The candidate kernel, API, test route, and
benchmark selector were removed. This is another case where an isolated bandwidth result cannot
override the authentic model gate. Evidence is in `artifacts/2026-08-24-q4-ffn-pair`.
The resulting research block shifts the next architectural axis to tree-shaped MTP verification.
Apple's family-9 profiling guidance directly supports the paired-kernel diagnosis: register and
thread-local storage pressure can reduce occupancy. BaseRT describes effective decode fusion as
removing both a launch and a global-memory round trip, which the rejected gate/up pairing did not
do. Staged Speculative Decoding instead reduces target-model bandwidth by arranging alternative
drafts as a tree within one verified batch.
Gem's MTP gate now records the official assistant's top-40 rank for the target token at each first
greedy mismatch. Across twelve hax-like rounds each, linear K=8 accepts 1.92 proposals for C code,
5.50 for terminal-agent output, and 2.08 for prose. For code, the target correction is in the
assistant top four on 9/11 mismatch rounds; for prose it is top four on 7/12. Terminal-agent output
already has nine fully accepted rounds. This supplies direct model evidence for bounded branching:
alternatives can help the low-acceptance cases without replacing the excellent linear fast path.
Evidence and cached paper text are in `artifacts/2026-08-24-mtp-acceptance-profile` and
`artifacts/2026-08-24-current-stage-profile`.
The diagnostic tree verifier now crosses the complete 60-layer 31B graph while remaining unreachable
from production. A dedicated RoPE operator assigns each node `base_position + depth`; the fixed-eight
F32 attention kernel then exposes only the windowed committed suffix plus that node's root-to-self
ancestry. The explicit-position RoPE operator matches its CPU oracle with non-monotonic offsets, and a
chain-shaped tree is bit-identical to the existing batched verifier across all 2,097,152 logits. For the
branched topology, all eight full-vocabulary distributions agree with independent sequential ancestry
runs: every top-1 token matches, cosine similarity is at least 0.999999882, and maximum absolute error is
at most 0.01973. The small nonzero branched differences are the established batch-eight MMA versus
batch-one matvec numerical path, not tree masking or position drift. Adjacent Metal RoPE and
FLASH_ATTN_EXT backend-op correctness pass, as do normal and ASan suites at 26/26. The next gate is an
adaptive server policy and one-axis target-throughput benchmark; production remains linear until that
gate passes. Evidence is in `artifacts/2026-08-24-mtp-tree-full-logits`.
The diagnostic commit operation also relocates a selected non-contiguous branch ancestry into logical
KV positions. After committing node 7 and evaluating a follow-up token, the complete 31B distribution
matches a fresh sequential path with the same top-1, cosine 0.99999999646, and maximum absolute error
0.00343. This proves cache relocation independently of scheduler quality.
An acceptance-window MTP-to-direct scheduler was tested and rejected in two forms. A four-round
warmup improved the low-acceptance prose replay by 16.45% but regressed code by 14.29% and agent
output by 6.50%. Extending warmup to eight rounds preserved the agent's MTP route but still regressed
code by 15.22% and erased the prose gain. Accepted-prefix history is nonstationary: direct rounds
advance only one token and can miss a later high-acceptance region. Both policy implementations were
removed. Future scheduling must optimize effective tokens advanced per target weight pass; tree
verification is preferable to direct fallback because even a mistaken branch decision retains a
multi-token main path. Raw interleaved three-repetition evidence is in
`artifacts/2026-08-24-mtp-adaptive-direct`.
A fixed-eight branched scheduler was then exercised end to end using the now-verified branch-KV
commit operation. The candidate retained a five-node main path and spread three rank-2 siblings
over its first three edges, selecting it after a low-confidence assistant round. It rescued branches
on prose, but the three-repetition 64-token gate regressed code by 6.41% and agent output by 7.54%; a
3.52% prose gain cannot offset those failures. More importantly, hybrid greedy generation was not
deterministic: the first observed divergence at generated token 21 selected token 1437 in one run
and the linear oracle's token 141562 in two runs. Switching batched numerical graph topology can flip
a near-tied argmax even though the isolated node-logit oracle preserves top-1 on its test vector.
The scheduler and harness were removed; tree attention, explicit RoPE, and branch commit remain
diagnostic-only. Evidence is in `artifacts/2026-08-24-mtp-tree-gate`.
The next fixed-graph verifier experiment tested whether an aligned native `half` load could replace
the explicit two-byte Q4_0 scale reconstruction without changing arithmetic. It was bit-identical
across all 2,097,152 verifier logits, but the isolated operator improved only 2.85%, below the 3%
promotion floor. Separate-process model timings showed severe thermal drift, so a temporary
same-process A/B/A harness alternated baseline, candidate, and baseline around every sample. Across
seven 31B repetitions, the direct load regressed verifier GPU latency by 4.10% against the mean of
its surrounding controls. The candidate pipeline and harness were removed. Evidence is in
`artifacts/2026-08-24-q4-verify8-halfscale`.
A new 56-stage timestamp trace now profiles the actual promoted one-command, device-sampled
seven-token MTP chain rather than the older synchronized single-token loop. Across three runs,
sampled GPU work averages 7.686 ms: the four assistant layers consume 52.34%, tied output plus
post-projection 29.01%, top-k40 15.19%, and embedding plus input projection 3.47%. Encoder-boundary
profiling intentionally perturbs total command time, so only the summed stage samples are used for
attribution. A single-axis 512-thread top-k block preserved all seven greedy proposals and passed the
445-case reference oracle, but increased top-k time from 1.167 to 1.336 ms (+14.43%) by doubling
the initial run count and adding a merge level. The 1024-thread production block was restored; the
stage profiler remains available through `gem-mtp-bench --device-chain`. Evidence is in
`artifacts/2026-08-24-mtp-chain-stage-profile`.
The trace now has 70 stages and splits each draft's output group into RMS normalization, tied
vocabulary projection, and recurrent post-projection. In the clean three-run baseline the tied
1024-by-262144 Q4_0 projection averages 2.203 ms for seven drafts. It accounts for more than 91% of
the output group; recurrent post-projection is only about 0.16 ms and normalization about 0.04 ms.
An exact-shape candidate doubled the matvec row fanout from four to eight. It preserved the native
CPU differential, all seven proposals, and all 1,154 adjacent Metal MUL_MAT oracle cases, but
regressed stable isolated throughput 2.00%, the authentic tied-output stage 5.97%, and total sampled
MTP work 3.69%. The candidate was removed. Higher row fanout likely spends too many registers or
reduces occupancy, so the next tied-output experiment should retain four-row scheduling and
specialize the fixed K=1024 control flow instead. Evidence is in
`artifacts/2026-08-24-mtp-output-row8`.
That fixed-K experiment is also complete and rejected. Retaining four-row scheduling while replacing
the generic block loop with one block per SIMD lane and removing output-tail tests reaches 446.51
GB/s across the four stable operator samples versus 448.60 GB/s for the control (-0.47%). The two
thermally stable authentic runs reduce tied-output time only 1.41%, below the 3% promotion floor; all
seven greedy proposals remain exact. The compiler already eliminates most fixed-geometry control
cost. The specialization was removed. Further work on this projection must change its memory access
or representation rather than merely constant-folding its control flow. Evidence is in
`artifacts/2026-08-24-mtp-output-k1024`.
A same-size SoA layout then transposed each four-row group's Q4 scales and sixteen payload bytes so
every load instruction addressed contiguous lanes. Separate-process measurements disagreed by run
order, so a temporary same-process A/B/A harness alternated SoA between two row-major controls.
After the first cold control, seven-repetition evidence gives 432.63 GB/s for SoA versus 456.77 GB/s
for the surrounding controls (-5.28%). Packing also costs 27.36 ms at startup and duplicates
150,994,944 bytes. The ordinary 18-byte stream already exploits cache-line reuse across its
sequential byte loads; transposition destroys that locality. The kernel, layout, API, and harness
were removed. Evidence is in `artifacts/2026-08-24-mtp-output-soa4`.
Keeping the exact row-major bytes but copying the 144 MiB matrix into a GPU-private Metal resource
was also screened. The private copy is bit-identical and takes 8.93 ms. Initial wall-only A/B/A
samples showed scheduling phases large enough to obscure the result, so the harness added Metal
command-buffer GPU timestamps. Across seven paired repetitions, private storage improves device
throughput only 0.36% and wall throughput 0.69%. This misses the promotion floor while duplicating
150,994,944 bytes. The private-copy API and harness were removed; shared mmap/no-copy remains
production. Evidence is in `artifacts/2026-08-24-mtp-output-private`.
Projection/top-k fusion was then tested as a stronger mechanism. The diagnostic fixed-shape kernel
launched 256 threadgroups of 1,024 threads; its 32 SIMD groups retained the proven four-row Q4
arithmetic, produced 1,024 logits in 8 KiB of threadgroup storage, and emitted the same 40
`(value, token)` pairs as the existing first sort. All 10,240 finite and 10,240 NaN/tie comparison
pairs matched exactly. However, stable same-process GPU timestamps measured 0.431587 ms fused
versus 0.406014 ms for the surrounding projection-plus-sort controls, a 6.30% latency regression.
The 1,024-thread grouping sacrifices more projection occupancy than the removed logits traffic and
dispatch recover, so the fused kernel and diagnostic API were removed. Evidence is in
`artifacts/2026-08-24-mtp-fused-projection-topk`.
That bounded top-k axis is promoted. The initial sort now writes 8-byte `(value, token)` pairs into
the 256-by-40 frontier, and all eight merge levels plus final sampling consume the carried values
instead of randomly gathering full logits. The diagnostic matched 40,960 ranked ID/value checks and
162 sampled-output checks across finite and NaN/tie-heavy inputs. Seven-repetition same-process A/B/A
GPU time falls from 17.442905 to 14.208964 ms per 100 selections (-18.54%). On the authentic 31B
seven-draft chain, three repetitions reduce top-k from 1.171972 to 0.991388 ms (-15.41%) with the
same proposals; wall time changes from 31.588 to 31.752 ms (+0.52%), inside the 2% guardrail. The two
scratch buffers grow by only 80 KiB total. Evidence is in `artifacts/2026-08-24-mtp-topk-pairs`.
Exact frontier truncation is promoted. Each pairwise node now emits only its best 40 entries, so the
eight levels write 10,200 pairs instead of 81,920 and the second scratch buffer shrinks from 80 to
40 KiB. Final frontiers and sampled outputs match the full merge for finite and NaN/tie-heavy inputs
at every power-of-two tree depth from 2 through 256 runs. Stable isolated A/B/A GPU latency improves
14.21%. A separate-process authentic screen had contradictory wall scheduling, so a stronger
same-model seven-repetition A/B/A gate alternated full/compact/full chains inside one loaded 31B
engine: top-k improves 13.79%, total GPU time 2.34%, and wall time 2.32%, with byte-identical sparse
distributions and proposals. Evidence is in `artifacts/2026-08-24-mtp-topk-compact`.
The exact four-way merge is promoted. Each 80-thread group merges child runs 0/1 and 2/3 into two
40-pair threadgroup lists, synchronizes once, then emits their exact best 40. Twenty-one complete and
missing-child shapes produce identical finite/NaN/tie frontiers and sampled outputs. Four levels now
replace eight, global merge writes fall from 10,200 to 3,400 pairs, and scratch B shrinks from 40 to
20 KiB. Stable isolated A/B/A GPU latency improves 3.50%. In a seven-repetition same-model 31B gate,
top-k improves 3.27%, total GPU time 0.54%, and wall time 0.48%, with identical sparse distributions
and proposals. Evidence is in `artifacts/2026-08-24-mtp-topk-fourway`.
Terminal merge/sampling fusion is complete and rejected. It retained the final 40 pairs in
threadgroup memory, removed one dispatch and the last global frontier write, and matched all 3,402
finite/NaN/tie sampled-output checks. Stable isolated A/B/A latency improved only 1.72% on-device and
1.82% by wall clock, below the 3% promotion floor, so no authentic-model gate was warranted. The
fused kernel and diagnostic API were removed; evidence is in
`artifacts/2026-08-24-mtp-topk-terminal-fusion`.
Top-k is now a bounded ~0.12 ms per-draft stage with no remaining dispatch-only proposal above the
promotion floor. Return to the updated 31B MTP stage trace and select the largest fixed-graph cost
that offers a stronger fusion or data-reuse mechanism.
The refreshed stable 31B trace assigns about 4.04 ms to the four assistant layers, 3.25 ms to output
norm/tied output/post-projection, and 0.83 ms to top-k across seven drafts; one of three independent
profiles suffered broad GPU interference and is retained as an outlier. The first layer-boundary
experiment fused post-attention RMSNorm, residual add, and FFN RMSNorm. A 4 KiB threadgroup staging
slice preserved the exact materialization boundary: 131,072 residual/normalized floats matched
bit-for-bit, and the isolated three-to-one dispatch benchmark improved 39.1%. Yet the authoritative
same-model 31B A/B/A gate improved assistant-layer time only 1.30%, total GPU time 0.69%, and wall
time 0.69%. The tiny elementwise dispatches are hidden by surrounding graph scheduling. The kernel,
API, and harness were removed; evidence is in
`artifacts/2026-08-24-mtp-post-attention-fusion` and the refreshed profile summary is in
`artifacts/2026-08-24-mtp-stage-refresh-after-topk`.
The assistant-specific gate/up-plus-GELU experiment is complete and rejected. One exact-shape
kernel streamed the two canonical 1024-by-8192 Q4 matrices together, retained the production
accumulation order, and wrote only `GELU(gate) * up`. It removed 56 dispatches per seven-draft
chain and roughly 96 KiB of F32 intermediate traffic per layer. The native operator differential
was bit-identical, all authentic proposals/candidate rows/probabilities matched, and the adjacent
Metal MUL_MAT oracle passed 1,154/1,154 immediately before the gate. Nevertheless, the three stable
same-model A/B/A triplets improved assistant-layer latency only 1.25%, sampled GPU and wall latency
0.95%, and command-buffer GPU latency 0.77%. The 9 MiB of Q4 weight traffic per layer dominates
the eliminated activation traffic, while the combined kernel raises accumulator pressure. It was
removed; evidence is in `artifacts/2026-08-24-mtp-gate-up-gelu-fusion`.
Dispatch-only and activation-only fusions are now exhausted for the assistant graph. Before another
kernel rewrite, add fine-grained authentic assistant-layer timestamps separating Q projection,
attention, output projection, gate/up, down projection, and epilogues. Select the next axis only if
that trace identifies reusable weight work or a stage large enough to clear 3% end to end.
The first fine-grained trace was subsequently found to have used a hardcoded F16 KV cache in the
benchmark harness despite its production-facing configuration record. Its five-token attribution
is retained only as an F16 short-context diagnostic and is explicitly superseded. The harness now
uses and reports the production F32 default. At a realistic 580-token context, the two stable 31B
runs place 4.409 ms of 8.048 ms assistant-layer time in attention (54.79%); a third broad GPU
interference run is retained as an outlier. All runs produced the same seven proposals. F16
attention mirrors were also slower at this context, by about 30% for SWA and 47% for the full layer,
so cache conversion is not a shortcut. Corrected evidence is in
`artifacts/2026-08-24-mtp-layer-fine-profile-f32-580`.
The first exact F32 attention screen changed only the 31B full-layer score geometry from eight heads
over one token to four heads over two tokens per workgroup. Native attention and Metal suites passed,
but median dispatch throughput improved only 0.33% (5713.68 to 5732.52/s), below the 3% isolated
floor. The candidate was removed before server integration or an oracle/model gate. Evidence is in
`artifacts/2026-08-24-mtp-score-head-block-screen`.
A second profiling pass split exact attention into cooperative-K score generation, global
normalization, and shared-V accumulation. Across three stable 580-token F32 runs, those stages
average 1.557, 0.136, and 2.730 ms respectively; shared-V alone is 34.41% of assistant-layer time
and all proposal sequences match. This follows FlashAttention-2's work-partitioning lesson but uses
target-hardware measurements to choose the axis. A GQA2 experiment then packed four KV heads into
one workgroup so all eight SIMD groups accumulated outputs and workgroup count halved. It was
bit-identical on wrapped 12B/31B SWA cases, but stable throughput regressed about 26% because added
indexing and synchronization dominated. The candidate was removed before an oracle/model gate.
Evidence is in `artifacts/2026-08-24-mtp-attention-substage-profile` and
`artifacts/2026-08-24-mtp-value-gqa2x4-screen`.
A simpler GQA2 follow-up kept one KV head per workgroup and doubled only the value dimension tile
from 64 to 128. It also remained bit-identical and halved workgroups, but regressed stable throughput
21.33%. The larger 17 KiB threadgroup tile and four accumulators per active lane reduced occupancy
or issue efficiency more than launch reduction helped. It too was removed. Together the two screens
show that the next shared-V candidate must reduce bytes or synchronization, not merely workgroup
count. Evidence is in `artifacts/2026-08-24-mtp-value-gqa2-d128-screen`.
The synchronization-only control doubled the shared-V token tile from 32 to 64 while preserving
one KV head, 64 dimensions, V traffic, and token accumulation order. It was bit-identical after the
64 weights per head were cooperatively staged. Stable wall/GPU throughput improved 0.97%/1.27%,
showing that fewer barriers help, but the 18 KiB threadgroup footprint caps the gain below the 3%
promotion floor. The candidate was removed. Evidence is in
`artifacts/2026-08-24-mtp-value-token64-screen`.
Token tile 48 then tested the occupancy-balanced midpoint: 13.5 KiB of threadgroup memory and about
one-third fewer barrier rounds than token 32. It was bit-identical and improved stable wall/GPU
throughput 1.99%/1.78%, better than token 64 but still below promotion. It was removed. The measured
32/48/64 curve closes token-tile tuning for this value kernel; a future candidate needs a different
dataflow rather than another nearby tile. Evidence is in
`artifacts/2026-08-24-mtp-value-token48-screen`.
A structurally different GQA2 value pass mapped two normalized query-head rows by 128 output
dimensions onto Metal simdgroup 8-by-8 matrix multiplies, using half inputs and float accumulation.
It passed CPU-relative wrapped-ring 12B/31B checks, but stable wall/GPU throughput regressed
40.92%/45.57%. Padding two useful rows to eight and converting/staging F32 cache values to half cost
far more than the matrix primitive saved. The candidate was removed before an oracle/model gate.
Do not retry a padded matrix path at decode's two-row GQA shape without native low-bit KV storage or
substantially more useful rows. Evidence is in `artifacts/2026-08-24-mtp-value-mma-screen`.
An implementation survey found an MIT-licensed Gemma-first Metal server with a custom Q3_0 format
(14 bytes per 32 weights versus Q4_0's 18) and native matvec kernels. A local diagnostic independently
adapted its split low/high bitplanes to Gem's four-row schedule. The native CPU/Metal differential
passed, and the adjacent Metal MUL_MAT oracle passed 1,154/1,154 immediately before the confirmation
screen. Nevertheless, exact assistant shapes regressed badly: 1024-by-8192 latency increased 71.76%
and 8192-by-1024 increased 56.61%. Bitplane extraction and reconstruction overwhelm the 22.22% byte
reduction on M4 Max. The entire diagnostic was removed before assistant conversion or acceptance
testing. Do not retry this Q3_0 layout; any future reduced-weight format must first prove efficient
native unpacking. Evidence is in `artifacts/2026-08-24-mtp-q3-weight-screen`.
The F16 KV path was revisited after direct Metal profiling showed that its remaining opportunity was
prefill scheduling, not merely cache width. A direct F16 split-GQA decode diagnostic held half K/V
tiles in threadgroup memory and improved the isolated 32K operator about 3.2%, but it reduced
authentic 31B generation throughput 8.71%; that decode route was removed. Keeping the existing
one-pass F16 decode and changing only batched prefill attention from one dispatch per row to an
exact split-GQA score/normalize/value sequence improved an interleaved 1,024-token gate from 49.14
to 82.22 pp t/s (+67.31%). Generation changed from 18.99 to 18.68 tg t/s (-1.62%), below the 2%
regression limit and inside the 4.04% baseline variation. The native batched CPU differential and
wrapped-ring 12B/31B full/SWA tests pass, and the adjacent `FLASH_ATTN_EXT` oracle passed
4,792/4,792 immediately before the gate. This is promoted as an explicit `--kv-type f16` server,
agent, and generation option; F32 remains the default because the authentic 2K comparison preserves
argmax but measures 0.998918 correlation and 1.91e-3 NMSE. Evidence is in
`artifacts/2026-08-24-f16-split-gqa`.
Low-bit KV research does not currently justify an int4 production path. Open-TQ-Metal reports a
fused Apple-Metal int4 attention design, but its own Gemma 4 31B quality study places the usable
int4 KV ceiling around 950 tokens and reports compounding error beyond it. The broader SRFT/int4
Apple study evaluates much smaller models rather than Gemma 4 12B/31B long-context serving. No int4
kernel was implemented: hax project prompts routinely exceed the published quality boundary, so a
future attempt first needs independent long-context Gemma 4 evidence rather than a speed-only
operator result.
With batch-one decode at the Q4 memory-streaming ceiling, the next production lever moved to
multi-turn hax scheduling. The server previously reset the target on every request, even though a
tool result or follow-up turn resends nearly the entire prior conversation. A naive live-sequence
cache correctly missed because raw generation contains channel-control tokens that OpenAI messages
canonically re-render. The promoted design instead checkpoints the exact tokenized request prompt,
streams the response, restores every K/V slot the bounded response could touch, and returns to the
request boundary. A later request reuses state only when its token sequence strictly extends that
exact prefix. Mismatches recompute from zero.
At approximately 3.1K prompt tokens, three real HTTP/SSE reset-based second turns took
70.216/81.469/70.722 seconds. Three growing cached turns took 1.024/1.050/1.096 seconds while
reusing 3,101/3,123/3,145 tokens and evaluating 22 new tokens each: mean latency falls 98.57%, a
70.14x speedup. A 1,025-token wrapped-SWA differential discards eight generated positions, restores
the prompt, and evaluates an alternate eight-token suffix. It preserves the same argmax with
3.29e-4 NMSE, slightly below the 3.56e-4 independent repeat-control NMSE;
full-model Metal execution was not bit-repeatable under the measured system load. Checkpoint plus
restore costs 2.57 ms for that tail. A real `gem agent`/hax tool loop then normalized
Gemma's un-delimited scalar `call:bash{command:pwd}` to valid JSON, reused 2,533 tokens on the tool
response request, returned the exact workspace path, and cleaned up the child server. Evidence is
in `artifacts/2026-08-24-exact-prefix-cache`.
The fixed 128-token response limit is now removed. A checkpoint performs no K/V copy; every target
evaluation lazily preserves the token-major slots it is about to write, and geometric storage growth
continues through the remaining model context. This covers direct decode, batch-eight verification,
correction, bonus, tree, and profiled evaluation paths at the inference boundary. If growth fails,
the response still completes and only that next-turn cache opportunity is discarded. A wrapped
1,025-prefix/160-discard authentic check retained argmax in three normal runs. The final ASan/UBSan
run measured 7.47e-5 restored NMSE versus 1.85e-4 independent-repeat NMSE. In a three-repetition
HTTP/SSE gate, every preparation response reached 160 tokens and every second turn returned `DONE`;
byte-identical 404-token full-prefill replays took 4.383/4.392/4.516 seconds, while exact hits took
2.305/2.338/2.529 seconds, reducing mean latency 46.04% (1.85x). Evidence is in
`artifacts/2026-08-24-lazy-prefix-rollback`.
A longer linear MTP lookahead is rejected after a complete K=16 diagnostic. The generic 16-row Q4
path merely matched two fixed-eight calls because it computes a padded 32-row tile. A dedicated
64-output by 16-row Metal kernel then passed the CPU differential and the adjacent 1,154/1,154
`MUL_MAT` oracle, and improved equivalent projection throughput 35--38%. That operator win did not
survive the authentic 31B gate. Across three deterministic 12-round runs, K=8 produced 28.44 t/s
with a 4.5-token mean accepted prefix, while K=16 produced 22.44 t/s with a 5.67-token prefix: a
21.11% throughput regression. The larger verifier cost outweighed its extra accepted work. The
fixed-16 kernel and all diagnostic inference APIs were removed; production remains K=8. Evidence is
in `artifacts/2026-08-24-q4-verify16`.
A refreshed canonical 31B scorecard used context 64, eight prompt tokens, eight generated tokens,
three repetitions, and native A/llama/A bracketing. Native F32 KV averaged 53.66 pp and 20.27 tg
t/s with less than 0.5% bracket drift. The pinned llama.cpp oracle (commit `d8df12e`) averaged
31.16 pp and 23.52 tg t/s with its required F16 KV: native prefill is 72.21% faster, while llama.cpp
direct decode is 16.05% faster. The already-promoted native K=8 MTP result of 28.44 t/s remains
20.92% above llama.cpp direct decode. llama-bench cannot select F32 KV, so this is competitor context,
not a one-axis KV comparison. Longer 64-token diagnostics crossed into a slower sustained machine
state and are excluded from the scorecard. Evidence is in
`artifacts/2026-08-24-current-31b-scorecard`.
The direct-decode gap motivated a bounded encoder synchronization screen. Replacing only the global
buffer-scope barrier before each one-row Q4 projection with a resource-specific activation barrier
passed all 29 normal tests, but the machine slowed 8.86% between the preceding and following
controls. Candidate decode was only 0.24% above the equally slow post-removal control, so no causal
gain could be established and the change was removed. Do not promote synchronization changes
without an interleaved selector that survives the 3% gate. Evidence is in
`artifacts/2026-08-24-q4-resource-barrier`.
The fastest promoted engine path is now connected to the preferred one-command offline workflow.
For `gem agent` with F32 KV and no explicit MTP option, a regular sibling named
`mtp-<target filename>` is discovered and passed through the existing strict assistant manifest
validation. Explicit `--mtp` wins, `--no-mtp` forces direct decode, F16 skips discovery, and
`gem serve` remains explicit. A real 31B hax smoke returned exactly `OK`, advertised native MTP K=8,
and left no listener behind. In the formal three-pair greedy gate, all six complete outputs were
byte-identical; direct process wall time was 4.19/4.04/4.06 seconds versus 3.29/3.35/3.31 seconds
with automatic MTP. Mean end-to-end latency fell 19.04% (1.235x), including model readiness, hax,
generation, and cleanup. Evidence is in `artifacts/2026-08-24-agent-mtp-autodiscovery`.
A completion audit found that real-model cancellation was required by the correctness ladder but
had no direct artifact. The server previously learned about disconnects only while writing visible
model events; Gemma control/reasoning tokens could therefore leave the serial GPU stream occupied.
The promoted fix sends content-free SSE keepalive comments before and after prompt evaluation and at
each generation-round boundary, and hardens every socket write with `MSG_NOSIGNAL`. A failed
heartbeat follows the existing prefix rollback/reset cleanup. In the authentic 31B MTP gate, curl
cancelled after one second with status 28; the same process then returned `/health` 200 in 1.003
seconds. A same-binary three-pair real-hax gate measured 4.453 seconds without heartbeats and 4.347
seconds with them (-2.40%, within noise), with six byte-identical outputs. A follow-up poll check
changed sequential recovery only from 1.003 to 0.956 seconds and was removed. Normal and sanitizer
tests include a forced mid-stream disconnect. Evidence is in
`artifacts/2026-08-24-real-cancellation`.
The deployed context-capacity gate now passes on the physical 128 GiB M4 Max for authentic 31B
Q4_0 plus the production K=8 assistant at 32K, 64K, and 128K F32 contexts. The configured KV
allocations are 7.05, 12.42, and 23.15 GB. Each server passed health and three requests using an
eight-token tokenizer-confirmed prompt; all nine completions stopped normally with identical text.
The two exact-prefix reuse requests at each capacity averaged 1.346, 1.350, and 1.351 seconds.
First-request measurements are retained but are order-dependent Metal/residency warmup, not a
context comparison. An ordered direct sweep reported 53.70/20.28, 53.67/20.02, and 53.45/18.39
pp/tg tokens per second, but bracketing runs alternated between roughly 18.3 and 20.1 tg at both
32K and 128K. That decode difference is therefore recorded as nonstationary drift, not a capacity
regression. The gate also exposed that the required `server-probe.sh` request used `stream=false`
while native serving rejected it. Native non-streaming completion aggregation is now implemented
for text, reasoning, tool calls, finish reason, and usage without changing the hax streaming path.
Normal and sanitizer suites pass 30/30 and the binary remains free of llama/ggml linkage. Evidence
is in `artifacts/2026-08-24-context-capacity-31b`.
The remaining later-tile attention hotspot was score generation: at a 2,496-token position it was
about 63% of both SWA and full-attention operator time. A fixed-31B q8k8 score candidate uses
Metal 8x8 SIMD-group matrices to reuse each staged K tile across eight query rows. Full attention
stores its regular 8x8 result directly; wrapped SWA uses a 16-token tile and a 1 KiB threadgroup
scatter area to translate absolute ring positions back to each row's local score indices. The
existing exact global normalization and shared-V passes are unchanged, and all other shapes fall
back to the prior scalar score kernel.
After adding the required post-store SIMD-group visibility barrier, the exact 2,496/32 score stage
falls 79.27% for full attention and 82.62% for SWA; complete three-pass operator time falls
50.28%/51.18%. The adjacent MTL0 FlashAttention suite passes 4,793/4,793 and 200/200 independent
native full/wrapped-SWA differential processes pass. Authentic 2K logits retain finite values,
identical argmax/top-10 order, correlation 0.999999687, and NMSE 4.50e-7. A pre-fix five-pair model
gate had looked eligible but is invalid because the stress race was not yet fixed. Post-fix 256/512
gates improve mean pp 4.44%/4.27%, both inside 6.54%/20.14% noise floors while unrelated host load
and thermal drift were high. Promotion is therefore withheld; production remains the scalar score
path and q8k8 remains an explicit diagnostic selector pending a stable post-fix model gate. Evidence
is in `artifacts/2026-08-24-full-score-q8k8-31b`.
A true streaming probe now separates curl's immediate header/assistant-role TTFB (roughly 0.6 ms)
from the first SSE delta containing content, reasoning, or a tool call. Before scheduling changes,
authentic warmed 31B K=8 visible TTFT was 523.4/519.6/502.5 ms at 32K/64K/128K, confirming that
allocated capacity is not the short-prompt cause. Token tracing showed four suppressed channel
pieces before the first visible token, so an early speculative-root experiment had no benefit and
was removed. The promoted scheduler instead emits every already-verified token before evaluating
the correction, bonus, or direct token that prepares future logits. A same-process interleaved 32K
gate reduced MTP visible TTFT from 519.8 to 460.4 ms (-11.42%) and completion time 3.78%; direct
TTFT fell 8.18%. All six normalized SSE pairs were identical. The diagnostic-free production gate
measured 467.1 ms mean TTFT, 10.14% below baseline, with 30/30 normal and sanitizer tests and a real
hax `OK` smoke. Evidence is in `artifacts/2026-08-24-streaming-ttft-31b`.
Long-prompt full attention first promoted a scalar `long8` path: at positions from 4,096 onward,
only the exact 31B F32 full-attention shape grouped eight query rows while 12B, SWA, F16, partial
tiles, and every other shape remained rowwise. It was bit-identical to the oracle and improved a
same-process 8K full-model gate from 32.90 to 37.08 pp tokens/s (12.70%).
The subsequent fused path failure was localized to selector routing, not attention math: its
pre-4K fallback accidentally used serial decode attention. After routing those tiles through the
rowwise oracle, the 4,128-token authentic 31B gate retains identical argmax and ordered top-10,
correlation 0.999999929, NMSE 5.61e-8, and byte-identical fresh-process repeats. The original fused
kernel also proved to be three complete K/V passes despite its online name and was slower than
long8. The promoted replacement implements the true one-pass running-max recurrence, rescaling its
denominator and 512-wide register accumulator as the maximum changes. Against long8 it improves
8K/32K/64K/128K full-attention tiles by 1.63x/1.61x/1.60x/1.59x and uses no score buffer. A
same-process interleaved 8K model gate improves pp from 37.03 to 41.25 tokens/s (11.39%) against a
2.29% noise floor; generation improves 3.35%. Normal and sanitizer suites pass 32/32, adjacent
MTL0 suites pass 4,793/4,793 FlashAttention and 1,154/1,154 matrix-multiply cases, native linkage
is clean, and a direct-server hax smoke returns exactly `OK`. Full-model pp plus streaming visible
TTFT at 64K/128K remain required before claiming end-to-end long-prompt completion. The promoted
default completes the three-repetition authentic 32K prompt gate at 31.700 pp tokens/s with samples
31.701/31.750/31.650 (0.13% coefficient of variation) and 20.40 one-token tg tokens/s, using a
32,800-token context and 7.05 GB KV allocation. The authentic 64K gate completes at 23.884 pp
tokens/s with samples 25.262/23.285/23.104 (4.09% coefficient of variation) and 20.04 one-token tg
tokens/s, using a 65,568-token context and 12.42 GB KV allocation. The first-to-later drop is
retained as sustained thermal/frequency drift rather than filtered as an outlier. An online
128K gate then completes at 14.705 pp tokens/s with samples 15.876/15.249/12.990 (8.43%
coefficient of variation) and 16.25 one-token tg tokens/s, using a 131,104-token context. The full
seven-hour ordered run therefore proves capacity and execution but also establishes a material
endurance/thermal warning; the third sample is retained rather than normalized away. An online
token-tile-8 experiment is rejected: its 32 KiB threadgroup allocation loses 2.6% at 8K and roughly
1.5-2.0% at 32K/64K versus the promoted tile-4 kernel, indicating occupancy costs exceed the saved
barriers. Its post-endurance 128K operator samples are thermally unstable and are not evidence.
Evidence is in `artifacts/2026-08-24-long-context-online-attention`.
The next long-context pass specialized the online recurrence by exact model geometry. For 31B/F32,
one workgroup now evaluates two query rows while retaining the same causal masks, running maximum,
denominator, and F32 accumulator. Seven interleaved samples show 1.58x attention speedup at 8K,
32K, and 128K (1.56x at 64K), still with zero score scratch. Repeated authentic 2K, 4K, and
4,128-token logits are bit-identical; at the routing boundary the candidate is byte-identical to
the established online path across all 262,144 logits. Under a heavily throttled sustained run,
the interleaved 8K model gate improves pp from 30.62 to 33.29 tokens/s (+8.72%), above its 5.80%
noise floor. A final candidate decode sample fell to 9.74 tokens/s even though this selector does
not execute during decode; it is retained as thermal drift and makes the secondary result
non-significant. The analyzer still marks prompt-primary promotion eligible.
The 12B full-attention geometry received its own F32/F16 online kernels rather than inheriting the
31B shape. At 8K/32K/64K/128K, F32 attention is 6.05x/5.99x/5.86x/5.90x faster than its rowwise
oracle; F16 is 27.00x/27.74x/26.66x/26.99x faster than its rowwise F16 oracle. The 12B/F32
candidate is deterministic at 2K, 4K, and 4,128 tokens. At the boundary it retains identical
argmax and ordered top ten with correlation 0.9999997 and NMSE 6.31e-7. Its interleaved 8K model
gate improves pp from 51.19 to 62.39 tokens/s (+21.88%) against a 2.45% noise floor; generation
changes -1.90% inside 5.26% observed variation. F32 12B and F32 31B specialized kernels are now
the production defaults. F16 and `f16-full` remain separately gated deployments: their operator
results establish scalability and memory viability, but not yet model-level promotion.
Final real `gem agent`/hax smokes supplied an 8K-plus raw prompt so both post-4K selectors actually
executed; 31B and 12B each returned exactly `OK`, and the launcher released both server ports.
A three-process 31B/F32 cold-server gate then confirmed exact 8,192-token usage in every response.
First-visible SSE content arrived at 227.592/278.878/238.427 seconds (248.299-second mean, 8.89%
CV), while completion averaged 249.788 seconds. The 22.5% first-to-second slowdown is retained as
sustained thermal/frequency drift. The immediate ~1 ms HTTP header event is again excluded from
model-visible TTFT. Fresh-process and prefix-extension gates at 32K/64K/128K remain separate
endurance measurements; they are not inferred from this 8K result or from operator throughput.
The mixed `f16-full` capacity mode then received representation-specific promotion gates. It keeps
SWA in F32 and stores only the context-growing full layers in F16, reducing 31B/12B 128K KV to
12.42/2.82 GB. For both sizes, candidate outputs are bit-identical across repeated 2K, 4K, and
4,128-token evaluations. At the first post-4K tile, online versus rowwise preserves argmax and
ordered top ten; 31B correlation/NMSE is 0.999999922/7.67e-8 and 12B is
0.999999853/3.25e-7. Same-process 8K model gates show why shape-specific F16 online attention is
mandatory: 31B pp rises from 17.25 to 29.19 tokens/s (+69.25%, 5.76% noise), and 12B rises from
29.14 to 57.28 (+96.59%, 4.57% noise). Decode means fall 7.41%/6.71%, but both changes remain
inside 9.09%/11.20% observed variation and the selector does not execute during decode. The
existing 31B mixed route is validated and the specialized 12B route is promoted. `f16-full`
remains opt-in on the 128 GiB target: it is the capacity-efficient path, while F32 row2 remains
the fastest absolute 31B attention kernel. Evidence is in
`artifacts/2026-08-25-long-context-f16-full`.
Final 8K-plus real hax smokes returned exactly `OK` for both mixed-KV model sizes and the launcher
released both ports, confirming the selected kernels through the production HTTP/SSE path.
The final 31B/F32 row2 server then passed a three-process 32K cold plus exact-prefix gate. Cold
first-visible TTFT was 1,163.851/1,192.594/1,185.567 seconds (1,180.671-second mean, 1.04% CV),
with 1,183.040-second mean completion and exact 32,768-token usage. In the same process, a strict
assistant/user extension reused all 32,768 prompt tokens and evaluated only 26 appended tokens.
Its visible TTFT was 1.817/2.377/1.997 seconds (2.064-second mean), a 572x reduction from cold
ingestion, and completion averaged 2.239 seconds. This validates the intended offline hax usage
model: initial repository ingestion is expensive, but subsequent turns are suffix-only when the
canonical conversation strictly extends. Evidence is in
`artifacts/2026-08-25-server-prefix-31b-row2-32768`.
The equivalent final 31B/F32 row2 64K gate also passes. Three fresh cold requests report exact
65,536-token usage and first-visible TTFT of 2,809.768/2,837.049/2,830.016 seconds
(2,825.611-second or 47.09-minute mean, 0.41% CV); completion averages 2,828.940 seconds. Each
strict second turn reuses all 65,536 tokens and evaluates only 26 appended tokens, reaching visible
output in 3.267/2.512/3.008 seconds (2.929-second mean) and completing in 3.191 seconds. Prefix
reuse therefore reduces visible latency by 965x at this rung. Evidence is in
`artifacts/2026-08-25-server-prefix-31b-row2-65536`.
## Decisions
- GGUF is the canonical container, not the execution engine.
- Text-only Q4_0 is the first end-to-end target for both sizes because official artifacts exist and
the block layout is small enough for a dedicated Metal path.
- 31B is the primary performance target; 12B shortens correctness/profiling iteration and remains a
fully supported deployment profile.
- Multimodal artifacts remain a later phase. The optional matching MTP sidecar is now a native,
promoted server acceleration path; direct decoding remains the fallback and correctness oracle.
- llama.cpp output provides differential evidence until the native engine has independent golden
vectors. Matching its throughput is a benchmark goal, not an architectural dependency.
## Open questions
1. Is M4 Max decode limited by Q4_0 weight bandwidth or command/dispatch overhead at batch one?
2. What command-buffer span minimizes launch overhead without harming cancellation latency?
3. For hax prompt shapes, when does a dedicated batched Q4_0 kernel overtake matrix-vector tiling?
4. Can the alternating 256/512 head dimensions share one Metal pipeline through function constants
without losing occupancy?
5. Does a derived packed layout earn enough throughput to justify conversion and duplicate storage?
## Primary sources
- [Google Gemma 4 12B configuration](https://huggingface.co/google/gemma-4-12B-it/blob/main/config.json)
- [Google Gemma 4 31B configuration](https://huggingface.co/google/gemma-4-31B-it/blob/main/config.json)
- [ggml-org Gemma 4 12B GGUF](https://huggingface.co/ggml-org/gemma-4-12B-it-GGUF)
- [ggml-org Gemma 4 31B GGUF](https://huggingface.co/ggml-org/gemma-4-31B-it-GGUF)
- [Gemma 4 technical report](https://arxiv.org/abs/2607.02770)
- [Google Gemma 4 canonical chat template](https://huggingface.co/google/gemma-4-E2B-it/blob/main/chat_template.jinja)
- [Google Gemma 4 prompt and tool-control format](https://ai.google.dev/gemma/docs/core/prompt-formatting-gemma4)
- [llama.cpp Gemma 4 graph](https://github.com/ggml-org/llama.cpp/blob/master/src/models/gemma4.cpp)
- [FlashAttention: exact IO-aware tiled attention](https://arxiv.org/abs/2205.14135)
- [FlashAttention-2: improved work partitioning](https://arxiv.org/abs/2307.08691)
- [Online normalizer calculation for softmax](https://arxiv.org/abs/1805.02867)
- [Accelerating Large Language Model Decoding with Speculative Sampling](https://arxiv.org/abs/2302.01318)
- [Draft & Verify: Lossless Large Language Model Acceleration via Self-Speculative Decoding](https://arxiv.org/abs/2309.08168)
- [BaseRT: native Metal LLM inference](https://arxiv.org/abs/2607.00501)
- [Profiling LLM inference on Apple Silicon across quantization formats](https://arxiv.org/abs/2508.08531)
- [metal-llm-server Q3_0 reference implementation](https://github.com/githubpradeep/metal-llm-server)
- [RIGEL: M4 Max Metal tensor-path characterization](https://arxiv.org/abs/2606.12765)
- [Apple Metal Performance Primitives Programming Guide](https://developer.apple.com/download/files/Metal-Performance-Primitives-Programming-Guide.pdf)
- [Apple: Optimize Metal apps and games with GPU counters](https://developer.apple.com/videos/play/tech-talks/111373/)
- [Apple: Discover Metal debugging, profiling, and asset creation tools](https://developer.apple.com/videos/play/tech-talks/111374/)
- [llama.cpp Metal Q4_0 register dequantization](https://github.com/ggml-org/llama.cpp/blob/master/ggml/src/ggml-metal/ggml-metal.metal)
- [Open-TQ-Metal: fused int4 attention on Apple Silicon](https://arxiv.org/abs/2604.16957)
- [When Quantization Is Free: Apple Metal int4 and SRFT study](https://arxiv.org/abs/2605.05699)
### 2026-08-25: four-row global-attention sharing is an operator win, not a production win
A diagnostic 31B F32 global-attention kernel assigns one SIMD group to every `(query row,
query head)` pair in a 1,024-thread group, so four causal rows share each staged K/V tile without
quadrupling per-lane query and accumulator registers. The native differential reports the same
`1.54594846192e-12` NMSE as the established row1/row2 online kernels and fresh repeats are
bit-identical. The adjacent MTL0 `FLASH_ATTN_EXT` oracle passed 4,793/4,793 cases. Against row2,
three-repetition operator speedups are 1.1574x at 8K, 1.1691x at 32K, 1.1622x at 64K, and 1.1549x
at 128K.
The required full-model 8K interleaved gate did not reproduce that stability. Row2 averaged
30.0493 prompt tok/s and row4 averaged 31.4023 tok/s (+4.50%), but paired row4 outcomes were
+16.28%, -0.43%, and -1.87%, with 5.89% candidate CV. Row4 therefore remains diagnostic and row2
remains the production default.
Primary-source review suggests the next axis should be sequence-parallel online attention, not a
larger row tile. FlashAttention-2 parallelizes a single head over multiple thread blocks to improve
occupancy. Gem's row4 geometry launches only 32 long-lived groups for a 32-row block; a two-segment
split-K form would launch 64, retain total K/V reads, write about 4 MiB of partial F32 online-softmax
state, then merge `(maximum, denominator, accumulator)` in a second kernel. Apple Family-9 guidance
also makes two smaller independent screens worthwhile: direct device-buffer reads instead of
threadgroup cache copies, and an explicit `max_total_threads_per_threadgroup` compiler constraint.
Evidence is in `artifacts/2026-08-25-long-context-row4`.
### 2026-08-25: row4 follow-up rejects sequence splitting and retains row2
The proposed occupancy screens were implemented as isolated one-axis variants. A two-segment
sequence-parallel row4 kernel increased the number of workgroups from 32 to 64 and merged roughly
4 MiB of `(maximum, denominator, accumulator)` state. It was slower than unsplit row4 by
0.69%/0.99%/0.13%/0.34% at 8K/32K/64K/128K. Reading K/V directly from device memory instead of
cooperative threadgroup staging regressed 8K by 390.71%, and a 512-thread row4 geometry regressed
8K by 9.32%. An explicit 1,024-thread pipeline constraint produced only an approximately 1%
operator improvement. These results reject the tested FlashAttention-2-style sequence split and
Apple Family-9 occupancy/cache variants for this exact 31B geometry; the temporary kernels and
APIs were removed.
The earlier row4 full-model result was resolved with a purpose-built long-prefix harness. It builds
independent row2 and row4 31B inference states over the same mapped model, performs an unmeasured
post-prefix warmup for each, restores exact prefix checkpoints, alternates three 32-token
extensions, includes the final logits projection, and compares output logits. At a 32,768-token
prefix, row2 took 2.370925/2.288364/2.186804 seconds (2.282031 mean), while row4 took
2.478137/2.320768/2.426694 seconds (2.408533 mean). Every pair favored row2; row4 regressed wall
latency 5.54%. Logits were identical (correlation 1, NMSE 0, argmax 26745). A preliminary run that
appeared to favor row4 is retained but marked invalid because it omitted warmup and final logits;
its first row2 sample alone paid a 3.805-second cold-path cost.
Row2 therefore remains the production 31B F32 global-attention kernel. Base row4 remains only as a
diagnostic comparator for the long-prefix harness. Attention row-sharing, direct-read, threadgroup
size, and two-way sequence-splitting axes are closed for this dataflow; the next long-prompt
optimization must begin with authentic stage-level attribution rather than another attention-only
microbenchmark. Evidence is in `artifacts/2026-08-25-long-context-row4-followup`.
### 2026-08-25: authentic stage attribution finds SWA dominance, but exactness blocks reuse
A new long-prefill profiler uses Metal stage-boundary timestamps over one production 32-token tile,
while preserving the authentic prefix position and separating the 50 sliding layers from the ten
full-attention layers. At a 4,096-token 31B/F32 prefix, three production row2 samples average
0.907931 GPU seconds and account for 99.65% of GPU time through stage samples. KV attention is
0.541941 seconds (59.90% of sampled time). Surprisingly, bounded 1,024-token SWA contributes
0.482717 seconds (53.35%), while growing global attention contributes only 0.059224 seconds
(6.55%). QKV projections, FFN gate/up, and FFN down/output contribute 8.38%, 13.67%, and 11.63%.
The cause is explicit routing: the long-context selector keeps the production global row2 kernel
but evaluates each SWA row independently. Restoring the established 32-row batched split-GQA path
for all SWA layers improved a corrected 4K prefix-extension gate 19.14%, but failed model-level
correctness (correlation 0.998577, NMSE 3.65e-3). A new 64-thread online SWA row-pair kernel then
passed its wrapped-ring operator differential at 7.09e-13 NMSE and the adjacent MTL0
`FLASH_ATTN_EXT` suite at 4,793/4,793. It improved the extension gate 37.62%, but recurrent use
across 50 layers amplified its tiny local error to correlation 0.984060 and NMSE 3.29e-2.
Partial batched routing did not provide a safe compromise. Two-of-five SWA layers improved 9.28%
but produced NMSE 2.30e-3; one-of-five improved 11.87% but produced NMSE 4.15e-3. The non-monotonic
accuracy curve makes further subset selection prompt-specific overfitting. All temporary selectors,
the online SWA kernel, its pipeline/API, and test extensions were removed. Production remains
rowwise SWA plus row2 global attention. Future SWA work must preserve the established rowwise/global
softmax arithmetic order while reducing dispatch or K/V traffic; isolated operator NMSE is not a
sufficient proxy for repeated 50-layer stability. Evidence is in
`artifacts/2026-08-25-long-prefill-stage-attribution`.
### 2026-08-25: barrier repair does not resolve batched-SWA model drift
Primary-source review reinforced two constraints for the next SWA design. Apple documents that
threadgroup barriers are required for shared-memory visibility even when a threadgroup is only one
SIMD group, and that a fixed launch geometry preserves SIMD grouping across the dispatch. The
FlashAttention and FlashAttention-2 papers support exact IO-aware tiling and adding query/sequence
parallelism, but they do not promise bitwise equivalence after reassociating softmax arithmetic.
Inspection found that `attention_prefill_normalize_f32` lacked the barrier present in the rowwise
normalizer between reading the reduced maximum and reusing shared memory for partial sums. The
barrier was restored. The native authentic 31B SWA operator then reported identical score hashes
and zero output NMSE for scalar-batched versus rowwise/chunked paths, and the adjacent MTL0
`FLASH_ATTN_EXT` suite passed 4,793/4,793 cases.
The real-weight 4,096-prefix, 32-token interleaved extension gate nevertheless reproduced the
previous rejection exactly. Batched SWA averaged 1.077240 seconds versus 1.274846 seconds rowwise
(15.50% lower wall time), but final logits had correlation 0.998576705593 and NMSE
0.00364940360302. The temporary selector was removed and production routing remains unchanged.
The barrier remains because it is required synchronization, but the evidence shows that an isolated
operator comparison does not reproduce the authentic multi-layer command-buffer failure. The next
diagnostic must locate the first differing layer/tile and compare score, normalized probability,
attention output, and post-commit KV state at that boundary. Evidence is in
`artifacts/2026-08-25-batched-swa-barrier`.
Sources:
- [Apple: Creating threads and threadgroups](https://developer.apple.com/documentation/metal/creating-threads-and-threadgroups)
- [Apple: Porting Metal code to Apple silicon](https://developer.apple.com/documentation/apple-silicon/porting-your-metal-code-to-apple-silicon)
### 2026-08-25: exact-order two-row K reuse further accelerates wrapped SWA
The promoted wrapped-SWA score kernel shares each staged K pair across two adjacent causal query
rows while retaining the production lane accumulation loop and `simd_sum` reduction for every dot
product. Normalization and P*V remain unchanged. It applies only after the 1,024-token ring wraps;
tile zero stays rowwise and pre-wrap nonempty tiles retain scalar batching.
The native wrapped 31B scores and outputs are byte-identical to the scalar route. A 7x30
interleaved operator gate reduces score time from 2.492 to 1.522 ms (-38.91%) and complete SWA
attention from 3.969 to 3.004 ms (-24.30%); the adjacent Metal suite passes 4,793/4,793.
Authentic model gates improve 32-token extension latency 7.02% at 4K and 7.14% at 32K. All three
32K pairs favor the candidate, and repeated plus cross-route logits are bit-identical (correlation
1, NMSE 0, argmax 26745).
A three-repetition 4K full-graph profile attributes the gain to the intended stage. Sliding-layer
KV-attention time falls from 190.399 to 154.146 ms (-19.04%), total KV-attention time falls 14.39%,
and sampled GPU time falls 1.80%. Only the wrapped portion of a 4K prompt uses this kernel, so the
32K model A/B remains the promotion timing rather than this attribution profile.
The production route is now
`rowwise-gqa4096+31b-full-online-row2+rowwise-first-tile+batched-swa-row2-score`. It passes 42/42
normal and 42/42 ASan/UBSan tests. Reprofile before choosing the next kernel because score and P*V
are now approximately balanced. Evidence is in `artifacts/2026-08-25-swa-score-row2`.
The fresh-process 64K server gate confirms that the operator and shared-prefix gains survive cold
deployment. Exact-order row2 scoring lowers visible TTFT from
2,809.768/2,837.049/2,830.016 seconds to 2,278.434/2,260.390/2,255.804 seconds. The means are
2,825.611 versus 2,264.876 seconds: -19.84%, or 1.248x, with every candidate sample faster than
every baseline sample and only 0.43% candidate CV. All requests report exactly 65,536 prompt
tokens. Each strict second turn reuses all 65,536 tokens and evaluates only 26 appended tokens.
Its mean visible latency changes from 2.929 to 2.731 seconds (-6.75%), but 11.60% candidate CV makes
that small prefix-performance delta inconclusive; exact suffix-only reuse is the acceptance result.
Evidence is in `artifacts/2026-08-25-server-prefix-31b-row2score-65536`.
- [FlashAttention: exact attention with IO-aware tiling](https://arxiv.org/abs/2205.14135)
- [FlashAttention-2: parallelism and work partitioning](https://arxiv.org/abs/2307.08691)
### 2026-08-25: first-tile isolation makes batched 31B SWA exact and production-safe
The first-divergence harness resolved the repeated-layer failure. With a production-built prefix,
rowwise and scalar-batched SWA produce bit-identical activations, logits, and committed K/V at
32-, 1,024-, 2,048-, and 4,096-token boundaries. A two-engine lockstep scan instead found the first
persistent difference at position zero: layer-zero residual NMSE was only
`5.4534158079e-13`, but layer one then committed different K and V, and the perturbation amplified
through the 60-layer stack. The earlier candidate had batched the empty-cache tile only when the
configured context made the SWA cache capacity 1,024, explaining why small-context operator and
model checks missed it.
The production route now evaluates the empty-cache 32-token tile rowwise and uses the existing
three-dispatch scalar batched split-GQA path for every later 31B/F32 SWA tile. No encoder rotation
or new approximation is required. A full 4,096-token lockstep scan across 128 tiles reports
bit-identical K/V in all 60 layers. The shared-prefix 4K gate improves the 32-token extension from
0.834935 to 0.632096 seconds (-24.29%, 1.3209x); the shared-prefix 32K gate improves it from
1.384851 to 1.183060 seconds (-14.57%, 1.1706x). Both routes are independently bit-deterministic
and final logits are identical.
Promoted stage profiling attributes the gain correctly. At 4K, the latest three-sample mean SWA
attention falls from the recorded 0.482717 baseline to 0.190399 seconds (-60.56%), while full
attention remains approximately 0.051 seconds. Profiled wall time is 0.554989 seconds versus the
recorded 0.914530 baseline; these non-interleaved stage values are attribution rather than promotion
evidence. The production
31B/F32 configuration is now
`rowwise-gqa4096+31b-full-online-row2+rowwise-first-tile+batched-swa`. Evidence is in
`artifacts/2026-08-25-swa-first-divergence`.
The promoted route also passes the deployed fresh-process 32K gate. Three cold visible-TTFT
samples are 900.264/914.477/949.774 seconds (921.505-second mean, 2.26% CV), versus the final
rowwise-SWA baseline mean of 1,180.671 seconds: -21.95%, or 1.281x. Exact-prefix extension samples
are 1.664/2.013/1.732 seconds (1.803-second mean), -12.65% versus the 2.064-second baseline mean.
All requests report the exact 32,768-token prompt, and every extension log records 32,768 reused
plus 26 appended tokens. Evidence is in
`artifacts/2026-08-25-server-prefix-31b-batched-swa-32768`.
### 2026-08-25: Metal 4 TensorOps are an M4 experiment, not an assumed replacement
Apple's current Metal 4 guidance adds one useful future axis but does not justify replacing the
existing M4 kernels without measurement. Inline tensor operations can retain a matrix result in a
cooperative tensor distributed across participating threads' register storage, avoiding an
intermediate trip through threadgroup or device memory. Apple also recommends static slice extents
when dimensions are known because they avoid dynamic per-element bounds handling. Those properties
could eventually help fuse dequantization, projection, and activation work in the fixed Gemma 4
graph.
The hardware qualification matters: Apple documents dedicated neural acceleration for TensorOps
on Apple10-family GPUs beginning with M5/A19, while older Apple GPUs run the same API through an
optimized shader fallback. Gem's target machine is M4 Max, and the current kernels already use
SIMD-group matrix operations. TensorOps therefore remains a separately gated compatibility and
performance experiment, not a presumed speedup. It also must not be mixed into the active
batched-SWA server comparison.
For SWA specifically, prior substage samples put normalization near 0.6% of attention time; fusing
softmax alone has almost no useful ceiling. After the fresh-server gate, reprofile the promoted
q8k8 score path and its value pass. Choose exactly one next axis from that evidence: improve Q/K
score tiling if scoring still dominates, or reduce normalized-score/value traffic if P*V now
dominates. Any fusion must preserve the now-established empty-cache first-tile arithmetic and pass
the lockstep K/V and backend-op gates.
Apple's profiling guidance narrows that choice further. Occupancy alone is not a verdict: correlate
it with limiter, utilization, and bandwidth counters. Low occupancy can mean resource exhaustion or
simply too little work; very high occupancy can still accompany cache thrashing. For the current
wrapped-SWA profile, score and P*V are now approximately balanced, so the next local experiment
must wait for an Instruments replay of the retained trace. If register or threadgroup pressure caps
occupancy, do not increase the row tile. If memory-read stalls dominate with headroom, screen exact-
order four-row K reuse. If normalized-score/V traffic dominates, keep the two-row score kernel and
screen a value-side reuse change instead. No local GPU experiment should overlap the 64K server
gate because it would invalidate the timing.
MLX provides an independent implementation-level signal for the same locality axis. Its January
2026 two-pass vector-SDPA change was tuned on M3 Ultra, M4 Max, and M5, and changed work ownership so
one threadgroup handles all queries associated with a given key. The stated purpose is to maximize
locality for very long GQA sequences. Gem's two-row score kernel already applies a bounded form of
that mapping by sharing each staged K pair across adjacent query rows. A four-row screen would extend
the same ownership principle while retaining Gem's per-dot-product accumulation order; MLX's fused
online-softmax arithmetic itself is not a correctness oracle because that arithmetic previously
failed Gem's authentic repeated-layer quality gate.
Sources:
- [Apple: Running inline ML operations in a shader with Metal 4](https://developer.apple.com/documentation/metal/running-inline-ml-operations-in-a-shader-with-metal-4)
- [Apple: Metal sample code](https://developer.apple.com/metal/sample-code/)
- [Apple: Porting Metal code to Apple silicon](https://developer.apple.com/documentation/apple-silicon/porting-your-metal-code-to-apple-silicon)
- [Apple: Finding your Metal app's GPU occupancy](https://developer.apple.com/documentation/xcode/finding-your-metal-apps-gpu-occupancy)
- [Apple: Measuring the GPU's use of memory bandwidth](https://developer.apple.com/documentation/xcode/measuring-the-gpus-use-of-memory-bandwidth)
- [Apple: Discover new Metal profiling tools for M3 and A17 Pro](https://developer.apple.com/videos/play/tech-talks/111374/)
- [MLX PR 3023: Faster two-pass SDPA](https://github.com/ml-explore/mlx/pull/3023)
- [MLX Steel attention kernel](https://github.com/ml-explore/mlx/blob/main/mlx/backend/metal/kernels/steel/attn/kernels/steel_attention.h)
### 2026-08-25: Open-TQ-Metal audit keeps Gemma int4 KV closed but exposes a decode axis
A Paperbridge search surfaced Open-TQ-Metal, a 2026 Apple-Silicon study and MIT-licensed reference
implementation. Its headline 128K result must be scoped carefully: the 128K quality/capacity claim
is for Llama 3.1 70B, while the paper's own ablation says Gemma 4 31B int4 and int8 KV fail beyond
roughly 950--1,000 tokens because its unit attention scale amplifies error through 60 layers. The
paper also states that Gemma long-context perplexity and retrieval quality were not evaluated.
This independently supports Gem's existing decision to keep F32 KV as the 128 GiB production
default and reject int4 KV for 64K/128K hax use. Top-1 parity at a moderate prompt is not sufficient
evidence for long-context quality.
The transferable part is sequence-parallel decode. Open-TQ-Metal partitions a single long KV scan
into 512-token chunks, emits partial `(maximum, denominator, accumulator)` state, and merges it in a
second ordered phase. That is distinct from Gem's rejected two-segment *prefill* row4 experiment:
at one-token 128K decode, the current full-attention route exposes only one long-lived workgroup per
query-row/head block and may have much more sequence-parallel headroom. Gem already preallocates KV
storage, so it also avoids the reference engine's reported per-token cache-concatenation penalty.
Before implementing anything, capture an authentic 31B decode-stage profile at a populated 64K or
128K position. Screen an F32 split-K full-attention decode kernel only if those ten global layers
exceed the 3% end-to-end promotion ceiling. It must retain Gem's F32 KV, exact causal coverage, and
the established logit/determinism gates; compressed-domain arithmetic is not part of that axis.
Sources:
- [Open-TQ-Metal paper](https://arxiv.org/abs/2604.16957)
- [Open-TQ-Metal HTML, method and limitations](https://arxiv.org/html/2604.16957v1)
- [MIT-licensed Gemma reference implementation](https://github.com/mutable-state-inc/gemma4metal)
### 2026-08-26: populated decode profiling selects full-attention P*V sequence parallelism
`gem-decode-profile` now builds a requested prefix once and uses exact KV checkpoint/restore for its
unmeasured warmup and every repetition. A 31B smoke reports one prefix build and four rollbacks for
three samples, removing the previous four-ingestion cost at 64K/128K. The new
`gem-decode-attention-profile` isolates the production F32 split-GQA score, normalization, and value
passes at exact 12B/31B full/SWA shapes without claiming synthetic data as a model-level result.
Both diagnostic binaries remain free of llama/ggml dependencies; normal and ASan/UBSan suites pass
43/43.
At the 31B full-attention 64K shape, seven 20-iteration samples put score at 3.154 ms (22.19%),
normalization at 0.115 ms (0.81%), and value accumulation at 10.947 ms (77.01%). Effective unique
K bandwidth is about 170.2 GB/s, while the value pass reaches only 49.0 GB/s. The 128K result scales
linearly and preserves the same split: 6.314/0.356/22.379 ms and 21.74%/1.23%/77.04%, with value at
48.0 GB/s. The bounded 31B SWA operator remains roughly 0.255 ms at a 1,024-token live window, so
the selected experiment must be gated to the ten context-growing full-attention layers.
This evidence refines the online-source transfer. llama.cpp's Metal vector decode divides KV over
32 workgroups and merges partial softmax state in a second kernel. MLX PR 3023 similarly increases
two-pass parallelism for very long sequences. MLX PR 4077, merged August 18, 2026, improves GQA-8
decode by reading each K/V byte once and reports up to 14% operator and 7% full-model gains at 32K
on 40-core Max hardware. Its register-pressure gate stops at head dimensions 64/128; Gemma 4 31B
uses 512. Gem's current separated score/value passes already share each K and V tile across all
eight query heads without holding eight 512-wide online accumulators in registers. Therefore the
next axis is the value pass, not a verbatim fused MLX kernel and not another score change. Its exact
31B full geometry exposes a cheaper first screen than split-K: the current 64-dimension tile launches
only `4 KV heads * 8 dimension blocks = 32` threadgroups on the 40-core target. A specialized
32-dimension tile would launch 64 groups, halve per-group V staging and accumulators, read every V
element once, and preserve each output dimension's token accumulation order. Screen that exact-order
geometry first. Sequence-parallel value accumulation plus partial-output reduction remains the
fallback if 64 dimension groups still leave bandwidth unused, because it changes reduction order and
requires extra scratch/dispatch. A populated full-model 64K profile must still establish the
whole-token ceiling before either implementation is promoted.
The populated 64K profile now establishes that ceiling directly. One authentic 31B token at
position 65,536 averages 243.076 ms of GPU time with 98.91% stage-counter coverage. KV attention
uses 183.675 ms, or 76.40% of sampled time. Combining that model result with the exact-shape global
operator profile puts the existing ten-layer full-attention value pass near 109.5 ms, approximately
45.5% of the sampled token. A value-only improvement of about 6.6% can therefore clear the 3%
model promotion floor.
The exact-order 32-dimension candidate clears its operator gate decisively. It reduces value
workgroup storage from 9,216 to 5,120 bytes and increases the exact global dispatch from 32 to 64
threadgroups without changing V bytes or per-output token accumulation order. An unwrapped
8,193-token CPU/current/candidate differential is bit-identical. In a 64K ABBA bracket with seven
20-iteration samples per arm, the existing value pass averages 11.621/11.147 ms and the candidate
8.009/8.001 ms, a bracketed improvement of roughly 29.7%; total three-stage attention improves
about 23.2%. Normal and ASan/UBSan suites pass 43/43, and the adjacent MTL0 `FLASH_ATTN_EXT`
oracle passes 4,793/4,793. Diagnostic selector 18 retains selector 17 prefill/SWA behavior and uses
the candidate only for 31B F32 global decode at 8K+. Its populated 8K smoke passes; the decisive
64K model profile reduces token GPU time from 243.076 to 229.678 ms (-5.51%), exceeding both the
3% floor and the larger 3.11% run CV. KV attention falls 13.62%. At 128K, the stable closing
operator bracket reduces P*V from 22.681 to 16.051 ms (-29.23%) and complete attention from 29.402
to 23.332 ms (-20.64%); all four raw arms are retained because the opening baseline frequency-
ramped. Authentic final logits after 8,193 tokens are byte-identical across selectors 17/18 with
matching SHA-256 `b9ea77055de12bd4a327158a7feb3656c1997f228728d8299f47d67142c4c87e`.
Selector 18 is therefore the promoted 31B/F32 default. Normal and ASan/UBSan suites pass 43/43
after promotion, while selector 17 remains the diagnostic rollback.
Long-position iteration now also has an explicitly non-promotional shortcut. The diagnostic
seeded-prefix API fills the already allocated KV buffer with finite bytes, advances the logical token
position, and leaves the production 60-layer graph and dispatch routing unchanged. A 64-position
smoke reports `prefix_mode: seeded-kv`, zero prefix builds, four checkpoint rollbacks, and the normal
1,383-dispatch decode graph. Normal and ASan/UBSan suites remain 43/43, including hax integration and
all native dependency guards. Use this mode only to screen stage timing and dispatch geometry while
the expensive evaluated-prompt 64K/128K gates remain authoritative.
PaperBridge retrieval of FlashInfer, FlashDecoding++, and Open-TQ-Metal refines the deferred
sequence-parallel design. Attention states over disjoint token ranges are composable, and the
Apple-specific Open-TQ implementation confirms that a separate partial/reduce dispatch avoids the
single-threadgroup 128K serial scan. Gem should not copy its compressed online-softmax kernel: the
production graph already computes globally normalized scores and shares each F32 V tile across all
eight query heads. The isolated next axis is therefore a split normalized P*V pass. At the exact 31B
shape, each token partition writes `32 * 512` F32 partial outputs and a deterministic second kernel
sums partitions in ascending order. Split counts 2/4/8/16 require only 128 KiB/256 KiB/512 KiB/1 MiB
of reusable scratch, raise the current 64 value threadgroups to 128/256/512/1,024, and keep aggregate
V reads unchanged. Test each count independently; do not combine it with a dimension tile, token
tile, score path, or KV representation change. Because partial summation reassociates F32 additions,
this route needs a nonmultiple-tail differential, repeated model-logit determinism, and the ordinary
quality thresholds rather than a claim of bit-identical arithmetic.
The 128K cold-server runner is now safely resumable because one repetition takes hours. Every new
gate writes `gate-config.json` binding model and prompt paths/digests, context, exact prompt count,
KV type, output limit, prefix-extension text, and repetition count. `GEM_GATE_RESUME=1` additionally
requires byte-equivalent canonical `print-config`, validates every completed measurement/SSE/log
tuple, and reruns only incomplete repetitions in fresh processes. A CPU-only copied-artifact test
accepts three valid completed samples while independently rejecting an occupied port, a changed
native route, and a changed prompt digest. That check is now the permanent
`gem.long_context_server_gate_resume` CTest rather than a one-off manual fixture. Shell syntax and
ShellCheck are clean, and the normal plus ASan/UBSan suites pass 44/44.
Long decode profiling now exposes prefix progress without changing its execution schedule. The new
progress API is a nullable callback inside the existing production evaluation loop and fires only
after each already-completed batch; the ordinary API calls the same implementation with no callback.
`gem-decode-profile` reports every 4,096 tokens to stderr by default and adds prefix wall time/rate to
stdout JSON. A 64-token smoke emits exactly the requested 32/64-token reports and retains one prefix
build, four checkpoint rollbacks, 60 layers, and 1,383 decode dispatches. Normal and ASan/UBSan
suites pass 44/44 after adding the server-gate resume regression.
The current web audit adds a useful warning to the deferred split-P*V plan. MLX's merged GQA-8
two-pass kernel reads each K/V subchunk for several query heads and reports a 7.0% full-model gain
at 32K on a 40-core Max, but it gates only head dimensions 64/128 because register pressure changes
the winning geometry; it does not validate Gemma 4's dimension 512. More importantly, a separate
MLX issue localized deterministic generation collapse to the exact boundary where another GQA
two-pass path became active. This is not evidence that Gem's separated normalized P*V split is
incorrect, but it makes threshold-crossing model tests mandatory. The screen must include positions
immediately below/at/above the route threshold, a nonmultiple partition tail, every candidate split
count, and repeated authentic logits. A block-count sweep alone is not evidence: a reported 100K
GQA shape remained at roughly 25--28% of practical reduction bandwidth across its sweep. Apple also
warns that neither high nor low occupancy diagnoses a bottleneck by itself; select the split count
from P*V wall time plus limiter/bandwidth counters, not workgroup count alone.
Residual selection is now machine-checkable rather than an informal multiplication. The new
`analyze-decode-residual.sh` binds an authentic populated-position profile to its exact-shape
operator receipt and accounts for the ten 31B full-attention layers. On the promoted 64K route,
current P*V supplies a 34.83% theoretical whole-token ceiling, so an 8.61% value-pass improvement
would be sufficient to reach the 3% whole-token floor. The operator accounts for 70.98% of measured
KV-attention time, remaining physically consistent. This admits screening but explicitly does not
promote a kernel. The same analyzer will consume the running authentic 128K receipt before any
split-P*V implementation begins.
The production server now reuses that batch-boundary progress API for long-prompt operability and
cancellation. Streaming requests receive a content-free SSE keepalive after every already-completed
prefill batch, so a disconnected hax client leaves at most one submitted batch before evaluation
stops; non-streaming aggregation keeps the same callback with a no-op heartbeat writer. Prompts of
at least 4,096 tokens log logical progress, evaluated-token throughput, elapsed time, and ETA every
4,096 appended tokens, including exact-prefix reuse in the logical numerator. The graph, batch size,
command buffers, and first-visible-event definition are unchanged. A failed callback now exits the
shared evaluation loop with an explicit cancellation error after preserving all already-committed
batches; the next serial request takes the ordinary cache-miss reset path.
The multi-hour server gate now treats the exact 100% prefill log record as part of each sample's
atomic evidence tuple, both immediately after a fresh request and during resume validation. An
exact-prefix extension must likewise log its extended logical prompt count at 100%, preventing a
truncated or stale server log from being accepted alongside otherwise valid SSE/measurement files.
The permanent CPU fixture now uses a 4,096-token sample and proves that deleting only this final log
record forces a fresh server attempt instead of incrementing `resumed_samples`.
An authentic regression runner is also prepared for the post-profile GPU window. It starts the
native server, submits a canonical long prompt with curl's bounded timeout, requires status 28 and
an observed SSE keepalive, waits a bounded interval for `/health`, and then requires a visible
16-token completion from a second request to the same process. It rejects a cancellation prompt
that reaches the exact 100% prefill record. Its retained config, partial stream, logs,
recovery measurement, and summary distinguish callback cancellation from merely killing and
restarting the server.
The complete runner is permanently covered by a serial CPU HTTP/SSE fixture: the first request
emits a keepalive and remains busy past curl's timeout, health cannot respond until that handler
returns, and the second request supplies visible content plus exact usage. Normal and sanitizer
CTest configurations pass this orchestration gate; authentic GGUF execution remains intentionally
separate.
The queued 31B/F32 receipt uses the exact 131,072-token server-gate prompt at context 131,328,
port 18085, a five-second disconnect, and a 60-second recovery ceiling; its command and runner hash
are frozen in `prefill-cancel-preflight-131072.json` before execution.
A non-invasive process sample at 51:33 establishes liveness for the pre-heartbeat 128K profiler
binary. Its 21.8 GiB physical footprint is stable below a 21.9 GiB peak, VM reports zero throttled
pages, the main thread is waiting in `MTLCommandBuffer waitUntilCompleted` from the production
spanned evaluation loop, and the Metal queue thread is inside IOGPU command-buffer submission. This
is active GPU prefix evaluation rather than a silent host deadlock. The compact receipt is retained
as `live-profile-liveness-51m.json`; the transient full process sample was not indexed.
The authentic populated 128K receipt completed after one prefix build and four exact checkpoint
rollbacks. Production dim32 decode averages 416.378 ms/token across 414.381/409.375/425.378 ms
samples (2.16% maximum mean deviation), with 99.43% timestamp coverage. KV attention averages
333.533 ms, 80.56% of sampled time. The matching exact-shape dim32 receipt attributes 233.321 ms
to the ten global-attention layers and 160.513 ms to their normalized P*V passes. That is a
physically consistent 69.95% of authentic KV-attention time and a 38.55% theoretical whole-token
value ceiling; only a 7.78% value-pass improvement is required to clear the 3% model floor.
Sequence-partitioned P*V screening is therefore admitted for splits 2/4/8/16 as separate diagnostic
axes. No split is promoted by this attribution result.
The frozen authentic cancellation gate then passed at the exact 131,072-token prompt. Curl timed
out with status 28 after observing four completed-batch keepalives; the still-running native process
returned `/health` 200 one second later and served a second request successfully. That recovery
request produced its first visible content event in 484.832 ms and completed in 567.813 ms with
14 prompt, 6 completion, and 20 total tokens. The cancellation request never logged 100% prefill.
This closes the real-GGUF cancellation invariant, not the separate three-process 128K latency gate.
Afterward all 46 normal CTests passed, and all 46 ASan/UBSan CTests passed.
Seeded-KV production-route cross-checks are also retained at 64K and 128K. They report 184.781 ms
and 294.909 ms mean GPU time respectively, with the expected 60 layers, 1,383 dispatches, zero
prefix builds, and four rollback checkpoints. These receipts prove that the production geometry is
operational at both capacities without another multi-hour prefix build. Artificial finite KV bytes
do not reproduce evaluated-model data, so these measurements are explicitly non-promotional and
must not be compared against the authentic populated-position timings.
Evidence is in `artifacts/2026-08-26-long-decode-profile`.
The cancellation receipt is in `artifacts/2026-08-26-prefill-cancel-31b-131072`.
The admitted split-P*V experiment is now implemented as diagnostic selector 19 without changing the
production selector 18. Its first dispatch partitions the already-normalized probability/value dot
product into contiguous token ranges while retaining the dim32 output geometry; a second dispatch
sums partials in ascending partition order. Splits 2/4/8/16 use 128 KiB/256 KiB/512 KiB/1 MiB of
reusable scratch. At 64K, retained three-repetition receipts reduce complete attention from
11.035 ms to 5.883 ms and value-plus-merge from 7.810 ms to 2.654 ms with split16. At 128K, the
stable baseline is 22.344 ms total and 15.819 ms value; split16 reaches 11.852 ms and 5.324 ms in
its stable closing arm, while split8 reaches 12.934 ms and 6.412 ms. Earlier arms show real GPU
frequency nonstationarity, so the populated-model gate remains authoritative.
Correctness is green before that model timing. The native operator differential covers the exact
8,192 threshold, 8,193 nonmultiple tail, every split count, and deterministic repeats. Selector 19
is byte-identical to selector 18 below threshold at 8,191 tokens. At 8,192 it retains identical
argmax and ordered top ten with correlation 0.999999999670 and NMSE 4.64e-10; at 8,193 the same
checks pass with correlation 0.999999999754 and NMSE 8.28e-10. Repeated selector-19 8,193-token
logits are byte-identical. The adjacent MTL0 FlashAttention oracle passes 4,793/4,793, and all 46
normal plus all 46 freshly rebuilt ASan/UBSan tests pass.
The current upstream recheck supports the observed geometry but does not supersede local evidence.
MLX now routes long GQA decode to a two-pass kernel and selects 1,024 first-pass blocks on its large
Apple device class at 65,536+ tokens; it also documents that its reduction consumes partials in
SIMD-width chunks. Gem's split16 produces exactly 1,024 value workgroups at the 31B dim32 shape,
which helps explain why it saturates where smaller splits do not. MLX's kernel still supports only
head dimensions 64/128 on its specialized GQA path, whereas Gemma 4 uses 512, so copying its block
policy or fused online-softmax state would be unjustified. Apple's current synchronization guidance
also confirms that a write/read dependency between partition and merge dispatches requires the
explicit buffer barrier already present here. With P*V reduced, the unchanged score pass becomes
roughly half the 128K operator; score/K work is the next kernel axis only after selector 19 clears
the populated-model and deployed gates.
The authentic populated 64K model gate now clears that promotion floor decisively. Selector 19
averages 177.717 ms/token across 157.732/204.886/170.532 ms samples, versus selector 18's retained
229.678 ms mean and 228.322/221.690/239.023 ms samples. This is a 22.62% whole-token reduction;
every candidate sample is faster than every baseline sample despite the candidate's 15.29% maximum
mean deviation. KV attention falls from 158.659 to 104.745 ms (-33.98%), timestamp coverage is
98.43%, and the receipt retains one authentic prefix build, four checkpoint rollbacks, 60 layers,
one command buffer, and the expected 1,393 dispatches. This admits the populated 128K confirmation
but still does not change the production default.
The matching authentic populated 128K receipt completes promotion. Selector 19 averages
304.832 ms/token across 296.518/300.203/317.775 ms samples, versus selector 18's retained
416.378 ms and 414.381/409.375/425.378 ms samples. Whole-token GPU time falls 26.79%, and every
candidate sample is faster than every baseline sample. KV attention falls from 333.533 to
226.145 ms (-32.20%); stage-counter coverage is 98.79%. The receipt retains exactly one evaluated
prefix build, four checkpoint rollbacks, 60 layers, one command buffer, and 1,393 dispatches.
Together with the threshold/tail/repeat logits, 4,793/4,793 adjacent oracle, and complete normal
and sanitizer suites, this promotes selector 19 as the 31B/F32 default. Selector 18 remains the
unsplit dim32 rollback. The production route now reports its additional 1 MiB reusable value-partial
scratch explicitly; no 12B, F16, prefill, SWA, tokenizer, GGUF, server, or hax protocol behavior
changes in this promotion.
The post-promotion real hax acceptance smoke supplies an 8,203-token raw prompt through `gem
agent`, forces direct target decoding, and returns exactly `OK`. The server reports complete prompt
progress, hax exits successfully, and the launcher leaves no listener on its port. A preceding
8,448-context attempt also completed prefill but correctly returned a context-full length finish
because hax's requested completion allowance exceeded the remaining envelope; the passing receipt
therefore uses context 9,216 and retains that earlier outcome only as an admission check. Evidence
is in `artifacts/2026-08-26-long-decode-profile/hax-smoke-production-split16-8k.txt`.
Sources:
- [MLX PR 3023: Faster two-pass SDPA](https://github.com/ml-explore/mlx/pull/3023)
- [MLX PR 4077: Read each K/V byte once in GQA-8 decode](https://github.com/ml-explore/mlx/pull/4077)
- [MLX issue 4022: two-pass GQA routing-boundary correctness failure](https://github.com/ml-explore/mlx/issues/4022)
- [MLX issue 3837: long-context GQA bandwidth and block-count sweep](https://github.com/ml-explore/mlx/issues/3837)
- [Apple: Finding your Metal app's GPU occupancy](https://developer.apple.com/documentation/xcode/finding-your-metal-apps-gpu-occupancy)
- [FlashInfer recursive attention and KV sequence parallelism](https://docs.flashinfer.ai/tutorials/recursive_attention.html)
- [FlashInfer paper](https://arxiv.org/abs/2501.01005)
- [FlashDecoding++ paper](https://arxiv.org/abs/2311.01282)
- [Open-TQ-Metal paper](https://arxiv.org/abs/2604.16957)
- [llama.cpp Metal vector attention](https://github.com/ggml-org/llama.cpp/blob/master/ggml/src/ggml-metal/ggml-metal.metal)
### 2026-08-26: upstream recheck keeps long-prefill attribution ahead of kernel selection
A web recheck during the final 128K server gate found no upstream result that should bypass Gem's
pending authentic stage profile. Current MLX uses a specialized two-pass GQA decode only for head
dimensions 64/128 and selects 1,024 first-pass blocks at 65K-plus keys on its large Apple GPU
class. That independently supports Gem's promoted split-16 decode parallelism, but it does not
transfer directly to Gemma 4's 512-dimensional Q/K and 32-dimensional V geometry. MLX's fused full
attention now covers dimensions through 256, while its own Gemma 4 head-dimension-512 report
documents an unfused fallback. A separate MLX long-prefill report also identifies the need to
chunk work across command-buffer boundaries to avoid multi-second GPU watchdog exposure.
BaseRT is useful architectural corroboration: its published native runtime uses sequence-regime
specific SIMD-matrix GEMMs, bounded prefill chunks, and tiled online-softmax attention. Its public
evaluation, however, stops at 2,048 prompt tokens and its production Metal engine is proprietary,
so it supplies neither a 64K/128K performance claim nor adaptable kernel source. Open-TQ-Metal's
fused int4 KV path remains a separate representation/quality axis; its own Gemma 4 analysis warns
that the model's attention scale amplifies angular quantization error. It must not be combined with
the current F32 kernel experiment.
The resulting order stays evidence-led: finish the frozen 128K cold/prefix gate; measure a seeded
128K production tile by projections, SWA attention, and full attention; then optimize the largest
stage. If full attention dominates, screen a bounded BQ/BK tiled online kernel specialized for
head dimension 512. If Q4 projections dominate, tune the existing 32-row Q4 GEMM instead. The
decode-only score-token-tile experiment remains isolated and cannot establish prompt-processing
speed.
Sources:
- [MLX Metal SDPA dispatch and long-GQA block policy](https://github.com/ml-explore/mlx/blob/main/mlx/backend/metal/scaled_dot_product_attention.cpp)
- [MLX head-dimension-512 fused-SDPA gap](https://github.com/ml-explore/mlx/issues/3885)
- [MLX long-context full-attention watchdog analysis](https://github.com/ml-explore/mlx/issues/3302)
- [BaseRT native Metal runtime paper](https://arxiv.org/abs/2607.00501)
- [BaseRT public repository and engine licensing boundary](https://github.com/basecompute/baseRT)
- [Open-TQ-Metal compressed-domain attention](https://arxiv.org/abs/2604.16957)
- [Apple Metal feature-set tables](https://developer.apple.com/metal/limits/)
### 2026-08-26: authentic 64K gate rejects direct, staged q8k8, and row4 prefill
Three bounded global-attention candidates were screened against the selector-19 row2 production
path without changing GGUF, graph, KV representation, or online-softmax semantics. Direct device
K/V reads were bit-identical but slower by 31.38% at 64K and 34.94% at 128K. The staged 8x8
score/value candidate was also bit-identical but slower by 67.86% and 67.69%. These results reject
both Family-9 cache hypotheses for the fixed head-dimension-512 geometry.
Four-row ownership looked promising only in synthetic evidence: it reduced the isolated global
operator by 14.92% at 64K and 14.35% at 128K, and a seeded 128K production-graph screen improved
11.05%. The authentic gate built one real 65,536-token selector-19 prefix, then interleaved three
checkpointed 32-token extensions. Row2 took 1.716713/1.700924/1.608773 seconds (1.675470 mean),
while row4 took 1.711669/1.698021/1.651547 seconds (1.687079 mean): row4 regressed 0.693% and
failed the 3% promotion floor. Outputs were bit-deterministic and the cross-route logits were
byte-identical, so correctness does not rescue the absent whole-model gain. Selector 21 and its
CLI/harness routing were removed; selector 19 remains production.
The installed Xcode beta now provides a usable profiling path even though the active developer
directory is Command Line Tools. `scripts/profile-metal.sh` discovers that Xcode without changing
global `xcode-select`, accepts an exact diagnostic command, and can select an alternate template.
A 128K seeded selector-19 Metal System Trace was captured. Adding Xcode's separate `Metal GPU
Counters` instrument activates counter profile 3 and shader-PC sampling, although the headless XML
does not expose occupancy or bandwidth aggregates. A new diagnostic `MTLCaptureManager` hook then
captured exactly one untimed row2 dispatch to a `.gputrace`; the profiler's timing samples are
completed before capture begins and its JSON marks `capture:true`. Use the 4K capture for resource,
spill, occupancy, and heat-map inspection because occupancy is geometry-dependent and GPU capture
serializes bound buffers. Retain the 128K system trace for long-context execution timing and memory
behavior instead of serializing a multi-gigabyte KV buffer into the GPU document.
Evidence is in `artifacts/2026-08-26-prefill-row2-direct-cache`,
`artifacts/2026-08-26-prefill-row4-production-128k`, and
`artifacts/2026-08-26-selector19-metal-trace-131040`.
Sources:
- [Apple: Capturing a Metal workload programmatically](https://developer.apple.com/documentation/xcode/capturing-a-metal-workload-programmatically)
- [Apple: Metal capture descriptor](https://developer.apple.com/documentation/metal/mtlcapturemanager/startcapture%28with%3A%29)
- [Apple: Metal performance heat maps](https://developer.apple.com/documentation/xcode/analyzing-apple-gpu-performance-using-performance-heatmaps-a17-m3/)
### 2026-08-26: Family-9 constraints refine the bounded F32 prefill candidate
Google's released Gemma 4 model card establishes that both dense targets have a 1,024-token sliding
window and support 256K context, not only the immediate 64K/128K acceptance rungs. Any production
prefill candidate must therefore keep scratch bounded independently of context length. The existing
q8k8 diagnostic's materialized score tensor remains useful attribution evidence but cannot become
the long-context route.
Apple's Family-9 guidance says threadgroup, device, and constant storage use the same flexible
on-chip cache hierarchy; explicit threadgroup staging is not inherently faster when the working
set already benefits from that cache. Apple separately identifies threadgroup memory and registers
as occupancy limiters. Consequently, the selector-20 16 KiB K tile remains a measured diagnostic,
not an assumed improvement. A new standalone `gem-metal-counter-info` tool records the target
device's public counter sets and supported sampling boundaries before counter instrumentation is
chosen. Xcode-only occupancy or limiter counters must not be treated as programmatically available
unless this receipt names them.
The existing all-F32 q8k8 score kernel and row2 online kernel expose a more specific fused design
if the seeded 128K profile attributes the largest stage to global attention. Preserve the row2 GQA
ownership: one 256-thread workgroup handles two causal rows and one KV head, with eight SIMD groups
owning the eight query heads. For each eight-token KV tile, one SIMD-matrix operation can form the
8-query-head by 8-token score matrix for each row. The eight SIMD groups then retain the same two
512-wide F32 output accumulators they own today and share the staged V tile. This keeps K/V reuse
across all eight GQA heads and both rows, halves tile barriers relative to the current four-token
loop, and maintains only a constant approximately 16.5 KiB threadgroup footprint. Online maxima,
denominators, and accumulator rescaling preserve exact dense attention without an O(context^2)
score buffer. This geometry is preferable to a workgroup per query head, which would reread each
shared K/V stream eight times. It is still only a design candidate until the 128K stage profile,
pipeline resource limits, oracle correctness, and one-axis ABBA measurements admit it.
Sources:
- [Google Gemma 4 model card](https://ai.google.dev/gemma/docs/core/model_card_4)
- [Apple Family-9 Metal shader performance guidance](https://developer.apple.com/videos/play/tech-talks/111373/)
- [Apple GPU occupancy guidance](https://developer.apple.com/documentation/xcode/finding-your-metal-apps-gpu-occupancy)
- [Apple SIMD-group matrix operations](https://developer.apple.com/videos/play/tech-talks/10858/)
- [FlashInfer attention-state composability](https://arxiv.org/abs/2501.01005)
### 2026-08-26: audited 128K service gate and stage profile select direct-cache full attention
The frozen selector-19 production binary passed the three-repetition 131,072-token HTTP/SSE gate.
Cold first-visible latency was 6,418.141, 6,322.454, and 6,388.358 seconds, averaging 6,376.317
seconds with 0.768% CV. The immediately following extension requests reused exactly 131,072 tokens,
appended 26, and reached their first visible event in 3.480, 3.394, and 3.342 seconds, averaging
3.405 seconds. The independent audit verifies the actual model, prompt, and frozen-binary digests,
all raw/summary measurements, SSE usage and finish reasons, `[DONE]`, and exact server log markers.
This is a 1,872.4x cold-to-prefix first-visible speedup and establishes that prefix reuse is the
critical operational path for repeated 128K hax work. The gate does not make cold ingestion fast:
that remains the next performance problem.
The subsequent seeded-KV production-graph profile measures one 32-token tile at position 131,040
without claiming synthetic cache contents as correctness or throughput evidence. Across three
repetitions, mean GPU time is 1.926 seconds with 0.395% CV and 99.865% timestamp coverage. KV
attention occupies 1.605 seconds, or 83.45% of sampled GPU time. The ten global layers account for
1.455 seconds; all 50 sliding layers together account for only 0.151 seconds. Projection-family
work is 0.303 seconds, or 15.77%. This selects only the global head-dimension-512 attention path
for the next one-axis experiment.
The target M4 Max reports Apple Family 9, 32 KiB maximum threadgroup memory, stage-boundary
sampling, and only `GPUTimestamp` in its public Metal counter sets. Occupancy, bandwidth, and
limiter evidence therefore requires Xcode GPU Trace. Apple's Family-9 guidance also says a
threadgroup-memory software cache can lose to direct device reads because both use the same
flexible on-chip hierarchy; Apple's occupancy guidance identifies both threadgroup allocation and
register pressure as possible launch limits. This corrects the earlier assumption that the
approximately 16.5 KiB staged q8k8 layout should be the first implementation.
The first bounded candidate should retain row2/GQA ownership and exact online softmax, but load
coalesced K/V directly from device storage so the Family-9 cache can reuse them across the eight
SIMD groups. It must specialize the fixed 31B geometry with function constants and preserve
context-independent scratch. Only after its resource report, correctness oracle, and ABBA screen
should a second one-axis variant add explicit K/V staging. Conditional online-softmax rescaling
from FlashAttention-4 is worth screening separately, but its Blackwell asynchronous pipeline and
software exponential are hardware-specific and should not be transplanted. FlashInfer's bounded
recursive state remains the right compositional model. Existing exact-shape data at 128K further
prioritizes V reuse: value accumulation is 77.04% of global attention, scores 21.74%, and
normalization 1.23%.
Evidence is in `artifacts/2026-08-26-server-gate-31b-split16-131072` and
`artifacts/2026-08-26-prefill-stage-attribution-31b-131040`.
Sources:
- [Apple Family-9 Metal shader performance guidance](https://developer.apple.com/videos/play/tech-talks/111373/)
- [Apple Family-9 GPU profiling workflow](https://developer.apple.com/videos/play/tech-talks/111374/)
- [Apple Metal Performance Primitives programming guide](https://developer.apple.com/download/files/Metal-Performance-Primitives-Programming-Guide.pdf)
- [FlashAttention-4](https://arxiv.org/abs/2603.05451)
- [FlashInfer](https://arxiv.org/abs/2501.01005)
- [MLX head-dimension-512 fused-SDPA gap](https://github.com/ml-explore/mlx/issues/3885)
### 2026-08-26: focused trace inspection and first-party recheck keep limiter replay mandatory
A narrow inspection of the retained row2 GPU capture found compiler fields for uniform and
temporary register counts, threadgroup memory, spilled bytes, and spill/reload passes. The capture
also retains the complete runtime Metal library and all compiled attention functions. Its headless
archive, however, does not expose a trustworthy mapping from the selected
`attention_prefill_online_gqa_31b_full_row2_f32` pipeline state to one compiler blob. Several blobs
contain spill diagnostics, including records with five compiler spills, but attributing any of
those records to row2 would be speculation. Replay the focused `.gputrace` in Xcode, group commands
by pipeline state, and read the selected shader's Performance timeline/heat map. Use the retained
128K System Trace for the authentic long-context limiter and bandwidth view.
The capture path now removes the remaining command-identification ambiguity. While capture is
active only, the production row2 dispatch pushes a debug group named exactly
`attention_prefill_online_gqa_31b_full_row2_f32`; the new labeled GPU document retains that string
in both its sorted and unsorted command streams. The profiler also queries the compiled pipeline
state and records execution width 32, maximum 1,024 threads per threadgroup, zero static
threadgroup memory, 16 KiB dynamic threadgroup memory, and the actual 256-thread launch. This rules
out a compiler maximum-thread limit below the launch geometry. It does not rule out runtime
occupancy-manager throttling from registers or the shared Family-9 cache, so counter replay remains
the selection gate. The query and its fixed dispatch invariants are covered by normal and
ASan/UBSan Metal tests.
The two authentically rejected row2 experiments are now fully retired rather than merely hidden
from public routing. Their Objective-C pipeline fields, MSL source strings, library concatenation,
pipeline creation, dispatch implementations, and cleanup references are removed. The resulting
clean labeled capture contains the production row2 debug group and neither rejected shader name.
This reduces the always-compiled Metal surface and prevents future compiler records from being
mistaken for active candidates. The post-cleanup gate passes all 56 normal tests, all 56 freshly
rebuilt ASan/UBSan tests, and the adjacent 4,793/4,793 FlashAttention oracle. No selector or
production dispatch geometry changed.
An installed `metalperftrace` follow-up cannot replace Xcode replay for this command-line compute
workload: live collection emitted no session and its one-minute always-on trace returned `No
session found`. Within the existing Xcode trace, raw counter rows belong to the default `RT Unit
Active` counter; the requested Performance Limiters profile has no decoded counter-info rows even
though shader-PC samples are present. Do not derive occupancy or bandwidth from those zero-valued
default rows.
Apple's current Family-9 workflow makes that distinction material. Low occupancy is actionable
only after correlating Compute Shader Launch limiter, Occupancy Manager Target, ALU limiters, L1
eviction/residency, last-level-cache stalls, MMU stalls, and memory bandwidth. Registers,
threadgroup storage, stack, and buffer data share the flexible on-chip cache hierarchy on this GPU;
therefore neither more threadgroups nor explicit staging is an optimization by itself. The public
pipeline properties can confirm execution width, maximum threadgroup size, and static threadgroup
memory, but they cannot replace those counter correlations.
Google's released 31B configuration independently confirms the exact long-context shape already
implemented by Gem: 60 layers in a five-local/one-global pattern, a 1,024-token local window,
262,144 maximum positions, 32 query heads, 16 local KV heads, four global KV heads, and
512-dimensional global heads. Thus 50 local layers remain bounded while ten global layers carry
the quadratic cold-prefill cost. The config sets `attention_k_eq_v` but no cross-layer KV sharing,
so reusing K as V inside each full layer is valid while sharing global caches across layers is not.
This recheck also closes a distracting hardware axis. Metal 4 TensorOps is portable, but Apple's
new neural-accelerator acceleration is specific to M5/A19-class GPUs; M4 executes its optimized
shader fallback. MPSGraph now exposes causal SDPA and could serve as a diagnostic oracle, but
introducing a graph framework into the production engine would violate the small fixed-graph
ownership goal unless a measured comparison first demonstrates a transferable kernel advantage.
For M4, the next implementation remains a custom bounded-memory global-attention candidate selected
from the captured limiter—not an M5 TensorOps rewrite.
Sources:
- [Google Gemma 4 31B configuration](https://huggingface.co/google/gemma-4-31B/blob/main/config.json)
- [Google Gemma 4 model overview](https://deepmind.google/models/gemma/gemma-4/)
- [Apple: Finding your Metal app's GPU occupancy](https://developer.apple.com/documentation/xcode/finding-your-metal-apps-gpu-occupancy)
- [Apple: Discover new Metal profiling tools for M3 and A17 Pro](https://developer.apple.com/videos/play/tech-talks/111374/)
- [Apple: Learn performance best practices for Metal shaders](https://developer.apple.com/videos/play/tech-talks/111373/)
- [Apple: Calculating threadgroup and grid sizes](https://developer.apple.com/documentation/metal/calculating-threadgroup-and-grid-sizes)
- [Apple: M5/A19 machine-learning acceleration](https://developer.apple.com/videos/play/tech-talks/111432/)
- [Apple: MPSGraph causal scaled-dot-product attention](https://developer.apple.com/documentation/metalperformanceshadersgraph/mpsgraphsdpadescriptor)
### 2026-08-26: full-graph gates reject a 128-row long-prefill batch
The production row2 operator was extended diagnostically from 32 through 128 rows without changing
its kernel. At position 131,040, 128 rows complete in 0.509994 seconds versus 0.144215 seconds for
32 rows, so isolated attention delivers 13.04% more rows per second. A seeded stage-profiled
60-layer graph initially reinforced that hypothesis: one 128-row tile took 6.275 seconds versus an
estimated 7.404 seconds for four 32-row tiles. Neither result is promotion evidence because the
first omits the graph and the second changes encoder boundaries for timestamp sampling.
The ordinary single-pass graph rejected the candidate. A real 4,096-token shared prefix followed by
interleaved 128-token extensions measured 6.765 seconds for four production 32-row tiles and 7.449
seconds for one 128-row tile, a 10.12% regression. Both routes were internally bit-deterministic and
their final logits were byte-identical. Seeded interleaved screens then measured batch-128 deltas of
-7.30% at 8K, -4.33% at 32K, +6.81% at 64K, and +22.29% at 128K. The critical long-context rungs
therefore reject the larger batch. All widened workspace, scheduler, SWA, benchmark, and harness
routing was removed; production remains 32 rows. The operator profiler alone retains rows through
128 so the scaling evidence remains reproducible without changing server behavior.
The divergence between stage-profiled and ordinary execution does not establish that a long command
buffer is intrinsically slow. Apple's compute documentation permits multiple dispatches in one pass,
and its MPS tuning guidance favors keeping command buffers queued instead of synchronously waiting.
Family-9 profiling guidance instead requires correlating occupancy and cache limiters. The next
bounded axis is therefore an interleaved batch-32 comparison of ordinary execution against selected
encoder/pass boundaries, with no timestamp counters. Only a measured win would justify attributing
the batch result to pass structure.
Evidence is in `artifacts/2026-08-26-prefill-batch128-screen`.
Sources:
- [Apple: Compute passes](https://developer.apple.com/documentation/metal/compute-passes)
- [Apple: Metal Performance Shaders tuning hints](https://developer.apple.com/documentation/metalperformanceshaders/tuning-hints)
- [Apple: Family-9 Metal profiling workflow](https://developer.apple.com/videos/play/tech-talks/111374/)
### 2026-08-26: unprofiled layer-pass segmentation is neutral and retired
The batch-128 screen left a possible confound: stage-attribution creates many compute encoders in
one command buffer, whereas production uses one compute encoder for the complete 60-layer tile. A
diagnostic candidate therefore kept selector 19, batch 32, every kernel, every dispatch, and one
command buffer fixed, but ended and reopened an ordinary compute encoder after embedding and each
model layer. It allocated no counter sample buffer.
At the critical seeded position 131,040, the same-process interleaved screen measured production at
1.908965/1.909300/1.934500 seconds (1.917588 mean) and layer passes at
1.929068/1.930033/1.908960 seconds (1.922687 mean). The candidate regressed 0.266%, while repeated
outputs were bit-identical and cross-route logits had correlation 1 and NMSE 0. Encoder boundaries
therefore do not explain the earlier batch-128 discrepancy and do not clear the 3% promotion floor.
The diagnostic Metal/inference APIs and harness routing were removed. Production remains a single
encoder and 32-row tile; the next optimization axis returns to the row2 kernel's per-thread state
and 16 KiB staged K/V footprint.
Evidence is in `artifacts/2026-08-26-prefill-layer-pass-screen`.
### 2026-08-26: serial K/V tile reuse is rejected
The production row2 kernel stages one four-token K tile and one four-token V tile concurrently,
using 16,384 bytes of dynamic threadgroup memory. A one-axis diagnostic reused one 8 KiB tile for
K and then V, with 256 bytes reserved for the 64 per-SIMD/per-row scores. This reduced the dynamic
threadgroup footprint to 8,448 bytes while keeping 256 threads, row2 ownership, the four-token tile,
F32 storage, and online-softmax update order fixed. The price was two additional uniform
threadgroup barriers per four cache tokens and a score store/reload.
Finite-data output was bit-identical to production row2, and the adjacent FlashAttention backend
oracle passed 4,793/4,793 cases. Interleaved three-repetition GPU timings decisively rejected the
candidate: it was 62.44% slower at 4,096 tokens, 61.32% slower at 65,536, and 60.52% slower at
131,040. The nearly context-invariant penalty identifies synchronization and score spilling as the
dominant cost; any occupancy benefit from halving threadgroup memory was insufficient. All
candidate shader, pipeline, API, test, and profiler routing was removed after recording the raw
JSON. Future row2 work should preserve concurrent K/V staging unless it removes barriers through a
different ownership or tiling scheme.
Evidence is in `artifacts/2026-08-26-row2-shared-stage-screen`.
### 2026-08-26: row2 token tile 8 wins the operator but misses the graph gate
The next one-axis candidate retained row2 ownership, 256 threads, eight SIMD groups, concurrent K/V
staging, F32 cache data, causal masking, and token-ordered online softmax while increasing the cache
token tile from four to eight. Dynamic threadgroup memory increased from 16 KiB to 32 KiB, but the
number of uniform threadgroup barriers over a long prefix was halved. Finite-data output remained
bit-identical and the adjacent FlashAttention oracle passed 4,793/4,793 cases.
The operator screen cleared its gate: tile8 improved GPU time by 3.23% at 4K and, over seven
repetitions, 4.51% at 64K and 4.41% at 128K. A seeded same-process full-graph harness then compared
32-token extensions through selector 19 with only the full-prefill tile changed. Logits were
bit-identical across repeats and routes, but wall time improved only 1.96% at 64K and 2.64% at
128K. Both mandatory long-context gains are below the 3% promotion floor, so the candidate was
rejected before an hours-long evaluated-prefix gate. Its shader, pipeline, API, selector, tests, and
profiler route were removed.
The generic `GEM_LONG_PREFIX_SEED=1` mode remains in `gem-long-prefix-ab`: it initializes finite KV
state once and uses checkpoint rollback for interleaved full-graph screening. Its receipts explicitly
report `prefix_mode: seeded-kv` and `prefix_builds: 0`; it is attribution evidence only and cannot
replace evaluated-prefix correctness or prompt-throughput gates.
Evidence is in `artifacts/2026-08-26-prefill-row2-tile8-screen`.
### 2026-08-26: current upstream converges on matrix-tiled query blocks for long prefill
The post-tile8 research block found a structural axis that is materially different from another
row2 staging or tile-size adjustment. FlashAttention-2 attributes low utilization to weak query
parallelism, non-matrix work, and cross-warp communication. The adjacent llama.cpp Metal oracle
implements that shape with eight query rows, 64 cache positions, eight SIMD groups, and 8x8
`simdgroup_matrix` multiply-accumulates. At the fixed Gemma 4 31B F32 head geometry (`DK=DV=512`),
its dispatch formula requests 28,672 bytes of threadgroup memory. This is a reference design and
correctness oracle only; Gem production remains independent C/Metal and does not link ggml.
A current upstream MLX recheck corrects an older note in this document. MLX's newer NAX attention
path is limited to head dimensions through 256, but head dimension 512 now remains on a fused Steel
Metal path rather than necessarily becoming unfused. That path selects `BQ=32`, `BK=16`, four SIMD
groups, and one output threadgroup per query tile and query head. Thus two independent maintained
implementations now support the same conclusion—batch query rows and perform QK/PV with matrix
fragments—while disagreeing on the best fixed tile. The disagreement is useful: tile geometry must
still be measured on Gem's exact 512/512, GQA-8, F32-cache workload.
The first bounded candidate will therefore be a specialized `BQ=8`, `BK=64` online-attention
kernel, owning one query head per threadgroup. It deliberately trades production row2's cross-head
GQA cache reuse for much lower per-lane register state and matrix QK/PV. If correctness and the
isolated long-context gate are sound, `BQ=32`, `BK=16` is the next one-axis geometry comparison.
F32 KV representation, causal semantics, selector-19 graph structure, and composable online
softmax state remain fixed. Open-TQ-Metal's int4-KV and split-K decode claims are excluded from this
prefill decision because they change cache representation and workload regime.
Metal 4.1 cooperative tensors are not the immediate target. Rigel's M4 Max measurements report
that they execute through the existing shader-core/simdgroup-matrix path rather than a separate
matrix unit, while their deployment requirements exceed this engine's current host path. The
portable 8x8 SIMD matrix primitive is therefore the appropriate first implementation target.
Evidence and the exact promotion order are in
`artifacts/2026-08-26-long-prefill-research-block/summary.json`.
Sources:
- [FlashAttention-2](https://arxiv.org/abs/2307.08691)
- [llama.cpp Metal flash attention](https://github.com/ggml-org/llama.cpp/blob/master/ggml/src/ggml-metal/ggml-metal.metal)
- [MLX Metal SDPA dispatch](https://github.com/ml-explore/mlx/blob/main/mlx/backend/metal/scaled_dot_product_attention.cpp)
- [Rigel M4 Max Metal 4.1 study](https://arxiv.org/abs/2606.12765)
- [Open-TQ-Metal](https://arxiv.org/abs/2604.16957)
### 2026-08-26: BQ8/BK64 matrix-tiled global prefill is promoted
The bounded `BQ=8`, `BK=64` candidate validated the structural hypothesis. One 256-thread
threadgroup owns one query head and eight query rows, stages 64 K/V positions at a time, and uses
8x8 SIMD-matrix operations for both QK and PV while retaining exact causal online softmax. Its
18,496-byte dynamic threadgroup footprint changes neither GGUF/KV representation nor the 31B graph
outside eligible F32 global-prefill tiles.
Finite-data attention NMSE was approximately 1.1e-12. Isolated attention improved 59.0-59.3% at
4K, 64K, and 128K. More importantly, the authentic evaluated graph improved 7.12% at 4K and
33.38% at 64K over three interleaved repetitions. The 64K comparison preserved argmax and the
top-10 set, produced correlation 0.99999958 and NMSE 8.35e-7, and was deterministic within each
route. Seeded attribution predicts the gain continues at 128K: 45.36% lower extension wall time.
Selector 19 now owns the BQ8/BK64 production route. Selector 22 preserves the former row2 global
prefill as a diagnostic rollback while sharing selector 19's SWA and split16 decode behavior. The
normal and sanitizer suites pass 56/56, the adjacent FlashAttention oracle passes 4,793/4,793, and
all native dependency guards remain green. This result follows the web-research direction directly:
query blocking and matrix fragments mattered much more than further scalar row2 staging tweaks.
Evidence is in `artifacts/2026-08-26-prefill-bq8-bk64-promotion`.
### 2026-08-26: promoted BQ8/BK64 passes 8K and 32K native-server gates
The promoted selector was then exercised through the real hax-compatible HTTP/SSE server with a
fresh process for every repetition and an exact-prefix extension on each process. At 8,192 prompt
tokens, mean first-visible TTFT was 206.982 seconds and the 26-token appended turn completed its
first visible event in 1.858 seconds after reusing all 8,192 prompt tokens. At 32,768 prompt tokens,
the three cold samples were 847.953, 827.775, and 819.124 seconds (831.617 mean, 1.779% CV); the
same exact-prefix extension averaged 2.804 seconds after reusing all 32,768 tokens.
Independent audits recomputed the aggregates from raw SSE and measurement files and verified every
100% progress record, prefix-hit record, model digest, prompt digest, and frozen production-binary
digest. Historical same-prompt production gates are 16.64% slower at 8K and 9.75% slower at 32K,
but those comparisons include intervening engine changes and are contextual rather than BQ8-only
promotion evidence. The evaluated shared-prefix graph remains the one-axis evidence.
The gate work also closed two provenance defects before the measurements: the audit now derives the
exact 31B F32 KV allocation from context and the fixed 50-SWA/10-global topology instead of
hard-coding the 128K allocation, and the runner now records and fail-closed verifies the production
binary digest on resume.
Evidence is in `artifacts/2026-08-26-server-gate-31b-bq8-8192` and
`artifacts/2026-08-26-server-gate-31b-bq8-32768`.
The critical 65,536-token rung subsequently passed with cold first-visible samples of 1,940.672,
1,957.977, and 1,914.561 seconds (1,937.737 mean, 1.128% CV). Every process then reused all 65,536
prompt tokens and evaluated only a 26-token appended turn; cached-extension first-visible latency
averaged 5.208 seconds, a 372.1x cold-to-warm reduction. The independent raw-artifact audit passed
all model, prompt, binary, progress, SSE, usage, and prefix-hit checks. This is 14.44% faster than
the closest historical row2-score production gate under the same model/prompt/context layout,
subject to the same intervening-change caveat. Evidence is in
`artifacts/2026-08-26-server-gate-31b-bq8-65536`.
### 2026-08-26: 128K BQ8 reliability passes, but host contention invalidates timing
The first promoted-BQ8 131,072-token server sample completed end to end, returned 16 tokens, then
reused the exact 131,072-token prefix and evaluated a 26-token extension. Cold first-visible time
was 6,459.234 seconds and the cached extension reached visible output in 4.331 seconds. This is
useful long-context capacity, correctness, streaming, and prefix-cache evidence, but not clean
performance evidence. Two individual 4,096-token intervals stretched by hundreds of seconds while
unrelated macOS services consumed multiple CPU cores. A second repetition encountered the same
condition and was stopped at 32,768 tokens rather than promoted as a benchmark.
The runner now writes a macOS host-state receipt before every 64K-or-larger sample, rejects a
one-minute load average above one runnable task per logical CPU, monitors that limit throughout the
request, and aborts after three consecutive violations. It also owns a `caffeinate -i -w` assertion
for the native server lifetime. These controls first appeared in schema 2; schema 3 added
the short-window CPU guard. Current schema 4 additionally requires AC power and no thermal or
performance warning before launch, monitors power and thermal state during the request, and makes
any recorded violation audit-fatal. The contaminated schema-1 directory is retained but cannot be
mixed with new repetitions.
Current primary-source research reinforces the existing matrix-tiled direction while narrowing the
next experiment. Apple's Metal 4 inline-ML guidance says Apple silicon does not need an explicit
software pipeline to overlap memory and compute, so Hopper/Blackwell warp-specialization should not
be copied mechanically. Apple's current Metal Performance Primitives guide instead emphasizes a
roofline model and multilevel SIMD-group/threadgroup tiling. FlashAttention-3's transferable idea is
overlapping matrix work with softmax; FlashAttention-4 further shows that shared-memory traffic and
exponential/rescale work can become dominant once matrix throughput improves. For Gem's already
promoted BQ8/BK64 kernel, the next one-axis screens should therefore measure conditional online
rescaling/exp reduction and a larger tile with explicit pipeline-resource receipts. BQ16 would
exceed the target's 32 KiB threadgroup-memory limit with the current F32 output layout, so the
bounded implemented geometry instead tests BK128 while retaining BQ8. CUDA-specific TMA, WGMMA,
tensor memory, and 2-CTA mechanisms are not applicable to the M4 implementation.
Sources:
- [Apple Metal 4 inline ML operations](https://developer.apple.com/documentation/metal/running-inline-ml-operations-in-a-shader-with-metal-4)
- [Apple Metal Performance Primitives programming guide](https://developer.apple.com/download/files/Metal-Performance-Primitives-Programming-Guide.pdf)
- [Apple power-assertion guidance](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/power_efficiency_guidelines_osx/PrioritizeWorkAtTheAppLevel.html)
- [FlashAttention-3](https://arxiv.org/abs/2407.08608)
- [FlashAttention-4](https://arxiv.org/abs/2603.05451)
Partial evidence is retained in
`artifacts/2026-08-26-server-gate-31b-bq8-131072`.
### 2026-08-26: exact conditional rescaling is rejected; BK128 remains diagnostic
Paperbridge located FlashAttention-4's conditional online-softmax rescaling and the separate AMLA
rescaling work. Gem first tested the conservative exact subset: skip `exp(0)` and the 4,096-float
threadgroup output multiply only when a 64-token tile leaves the running maximum literally
unchanged. Finite-data output was bit-identical to BQ8/BK64 and the adjacent Metal oracle passed
4,793/4,793. Seeded median deltas were +0.61% at 4K, +0.04% at 64K, and -1.01% at 128K. This is
well below the 3% floor, so the candidate pipeline was removed. The result indicates that the
threadgroup rescale is not the dominant standalone cost on this M4 Max path.
The operator profiler now supports `--seed-prefix`, which writes a deterministic sparse finite
signal into Q/K/V. This prevents data-dependent optimizations from being judged only on the
all-zero case, where the running maximum never changes after the first tile.
A second one-axis diagnostic doubles only the cache tile from BK64 to BK128 while retaining BQ8,
F32 KV, exact causal online softmax, one-head threadgroup ownership, and all graph routing. It uses
20,544 bytes of dynamic threadgroup memory and achieves finite-data NMSE 1.09e-12 with deterministic
repeats; normal and sanitizer suites pass 56/56 and the adjacent oracle again passes 4,793/4,793.
An initial contaminated screen reported a 3.38% mean improvement at 128K, but cannot be promoted:
the same 64K process drifted from 46.8 ms to 355.5 ms as macOS background work changed. BK128 is
therefore retained only as a diagnostic pending a quiet-host interleaved rerun. Production remains
BQ8/BK64.
Evidence is in `artifacts/2026-08-26-bq8-conditional-rescale-screen` and
`artifacts/2026-08-26-bq8-bk128-screen`.
The clean retry is now frozen in `scripts/run-bq8-bk128-gate.sh`. It uses ten warmups and ten
measured interleaved passes at 4K, 64K, and 128K, so BK64 and BK128 each run first equally often.
It records binary/source digests plus macOS load, power, thermal, assertion, and process receipts
before and after every context and rejects either a busy preflight or postflight. Operator-gate
schema 2 now fails closed unless every receipt is on AC power and reports no thermal or performance
warning; the analyzer validates those controls independently and embeds the digest/linkage receipts
in its decision. It caps per-arm coefficient of variation and half-run drift at 3%, requires both
64K and 128K improvements to exceed 3% and observed noise, requires the paired 95% confidence lower
bound to remain positive, and guards against a 4K regression beyond 2%. A pass
only advances BK128 to the authentic evaluated-graph/logit gate; it cannot directly change selector
routing. The normal and supported macOS ASan/UBSan suites now pass 60/60 including the analyzer's
positive, regression, paired-noise, failed-power, failed-thermal, and failed-postflight fixtures. A live runner smoke correctly refused the
current host at load 45.71 across 16 logical CPUs before launching any Metal work.
A later observation showed why load alone was insufficient: the one-minute value fell to 9.90 while
YAMS indexing and media analysis still consumed about four CPU cores according to the process
snapshot. The first schema-3 implementation gated that sum, but the macOS `ps` manual defines
`%CPU` as a decaying average over up to one minute—not an instantaneous value. The corrected
operator and server gates retain the sum only as provenance and gate a one-second `top` system-busy
sample instead, with a 10% default ceiling. Operator postflight applies the same guard. This closes
load-average lag without allowing stale process decay to reject an otherwise quiet host.
The adjacent DeepSeek-V4-enabled llama.cpp checkout at commit
`d8df12ebc4592b55dcecf97a32953623d031efdc` independently converges on the same Apple Metal
geometry for the relevant shape. Its FlashAttention constants use eight queries per threadgroup
and 64 cache values per SIMD group (`OP_FLASH_ATTN_EXT_NQPSG=8`,
`OP_FLASH_ATTN_EXT_NCPSG=64`), and its dispatcher selects eight SIMD groups when the head dimension
is at least 512. Gemma 4 31B has a 512-element Q head, so Gem's production BQ8/BK64/8-SIMD-group
shape matches all three choices. The adjacent code computes threadgroup memory from these constants
and retains BK64 rather than increasing the cache tile. This is design-oracle evidence, not a Gem
benchmark and not a runtime dependency; BK128 still receives the frozen local gate. If BK128 fails,
the reference strengthens the decision to remove it rather than keep speculative compiled surface.
The relevant reference locations are `ggml-metal-impl.h:112-113` and
`ggml-metal-ops.cpp:2992-3105` in the adjacent checkout.
The live `gem-metal-counter-info` receipt reports the Apple9 limit as 32,768 bytes, matching
Apple's current feature-set table. BK64 therefore uses 56.45% of the maximum memory available to
one compute threadgroup and BK128 uses 62.70%. Apple's API describes this as a per-kernel
threadgroup maximum, while `maxTotalThreadsPerThreadgroup` is a pipeline-specific dispatch limit;
neither value states how many workgroups reside concurrently on a GPU core. The formal analyzer
records the utilization and explicitly leaves occupancy unknown rather than deriving a false
resident-workgroup count. Xcode GPU capture remains the occupancy source.
Sources:
- [Apple `maxThreadgroupMemoryLength`](https://developer.apple.com/documentation/metal/mtldevice/maxthreadgroupmemorylength)
- [Apple `maxTotalThreadsPerThreadgroup`](https://developer.apple.com/documentation/metal/mtlcomputepipelinestate/maxtotalthreadsperthreadgroup)
- [Apple Metal feature-set tables](https://developer.apple.com/metal/feature-sets/)
An explicit “enable fast math” experiment is closed before implementation. Gem compiles the Metal
library with default options (`options:nil`). Apple documents `fastMathEnabled` as true by default
and says that state maps to fast math mode and fast FP32 functions; the newer `mathMode` property
replaces the deprecated Boolean. Therefore creating an options object merely to request fast math
would not change the compiled axis. A future scalar-exponential experiment must compare a genuinely
different implementation and demonstrate logit safety, not relabel the existing compiler default.
Sources:
- [Apple `fastMathEnabled`](https://developer.apple.com/documentation/metal/mtlcompileoptions/fastmathenabled)
- [Apple `mathMode`](https://developer.apple.com/documentation/metal/mtlcompileoptions/mathmode)
A bounded quiet-window watcher then sampled the host every 30 seconds. All eleven observations
failed the initial decaying-process-CPU guard, ranging from 186% to 362%; none began the Metal
profiler, and no clean artifact directory was created. These values are retained as evidence of
background activity, not described as instantaneous. The corrected short-window watcher must still
find a valid interval before the BK128 decision can proceed.
### 2026-08-26: Metal startup compilation is measured and deprioritized
Gem's Metal backend previously compiled a single 131,435-byte source string and created every
compute pipeline synchronously without phase attribution. `gem_metal_open()` now records device,
source assembly, runtime library compilation, pipeline creation, command-queue creation, and total
wall time. `gem-metal-startup-profile` emits those values as JSON without loading a GGUF or changing
backend selection.
Three fresh-process diagnostic runs on the M4 Max measured 51.7--58.0 ms total. Device creation was
43.6--49.1 ms, runtime source compilation 1.79--1.99 ms, and all pipeline creation 4.27--5.52 ms.
Three opens in one process fell from 60.7 ms to 4.3--4.5 ms after the first open. The host was not
quiet enough for a promotion benchmark (one-second system busy was 15.0% and load was 30.68), but
the result is sufficient for prioritization: even eliminating all observed compilation and pipeline
creation would save only about 6--8 ms from fresh server readiness. Offline `.metallib` packaging
and `MTLBinaryArchive` remain valid deployment improvements, not the next long-prompt performance
axis.
Apple documents `newLibraryWithURL:error:` as loading a precompiled `.metallib`, whereas
`MTLBinaryArchive` is a container for pipeline descriptors and associated compiled shader code.
They address separate phases and should not be implemented as one unlabeled optimization. If hax
readiness later becomes material, benchmark precompiled library loading first, then add a binary
archive as an independent axis.
Sources:
- [Apple precompiled Metal library loading](https://developer.apple.com/documentation/metal/mtldevice/makelibrary%28url%3A%29?language=objc)
- [Apple `MTLBinaryArchive`](https://developer.apple.com/documentation/metal/mtlbinaryarchive)
- [Apple binary-archive serialization](https://developer.apple.com/documentation/metal/mtlbinaryarchive/3553928-serializetourl?language=objc)
### 2026-08-26: BK128 now has an authentic-graph diagnostic route
The BK128 operator previously had no way to satisfy its required evaluated-graph/logit gate: it was
callable only from `gem-long-attention-profile`. Diagnostic selector 23 now preserves selector 19's
SWA, decode, batching, F32 KV, and rollback behavior while changing only the 31B global-prefill
dispatch from BQ8/BK64 to BQ8/BK128. `GEM_LONG_PREFIX_CANDIDATE=bk128` exposes a shared-prefix,
interleaved selector-19/selector-23 comparison. Production still initializes selector 19 and no
server option selects 23.
A deliberately non-promotable seeded 4K smoke exercised the complete graph. Both routes were
bit-deterministic, retained argmax 100 and the same top-10 set, and measured correlation
0.999996027 with NMSE 2.31e-5. Timing varied too widely to interpret (individual route samples
ranged from 3.12 to 5.36 seconds under heavy background work), so no performance artifact or claim
was retained. This proves the next gate is executable; it does not bypass the quiet-host operator
decision or the evaluated 64K prefix requirement.
The complete post-route suites pass 60/60 in both release and the supported macOS ASan/UBSan
configuration. The added candidate-contract test ensures `bk128` remains an accepted diagnostic
mode while unknown modes fail before model loading.
The authentic graph step is now a frozen artifact gate rather than a manual harness command.
`run-bq8-bk128-graph-gate.sh` independently re-analyzes the raw operator directory before loading
the model, captures exact binary/source/model/configuration/linkage provenance, and samples the
load, short-window CPU, AC-power, and thermal controls before, continuously during, and after the
evaluated 64K prefix. Its analyzer rejects changed selector identities or model controls and
requires correctness plus a gain above 3%, both arm CVs, and the paired df=2 95% lower confidence
bound. Passing still only permits an internal selector-19 implementation change; it does not itself
alter production.
A bounded post-verification watcher sampled the host twenty times from 05:38:51Z through 05:49:29Z
on 2026-08-27. Load per logical CPU ranged from 0.274 to 1.792 and eventually remained well below
the 1.0 ceiling, but the independent one-second CPU-busy sample never passed: it ranged from 13.01%
to 29.65% against the 10% ceiling. The watcher therefore launched no Metal workload and created no
`2026-08-26-bq8-bk128-clean` directory. This is retained as host-state evidence, not a BK64/BK128
performance result.
### 2026-08-27: adjacent vector attention narrows the next decode design
The adjacent DeepSeek-V4 llama.cpp oracle remains at
`d8df12ebc4592b55dcecf97a32953623d031efdc`. Its small-query Metal dispatcher selects the vector
path below 20 query rows whenever the head size is divisible by 32. That path stripes the KV
sequence across 32 workgroups, increases from one to four SIMD groups per workgroup as context
grows, carries exact online-softmax `(M,S,O)` partials, and combines them in a second kernel.
It is not a drop-in Gemma 4 global-decode kernel. Although the adjacent source instantiates the
correct symmetric `DK=DV=512` F32 shape, it assigns only one query head per threadgroup. For global
GQA-8 that rereads each shared K/V row for every query head. Gem's current score kernel instead
stages each 512-float K row once and shares it across all eight query heads; its production
"dim32" value name denotes a 32-element workgroup dimension tile, not `DV=32`. A future fused
split-KV diagnostic should therefore combine the adjacent cross-workgroup online-softmax reduction
with Gem's existing eight-head cache reuse, not copy the oracle kernel.
Before that larger axis, selector 20's already-implemented score-tile8 diagnostic must be resolved.
Its runner previously lacked host controls and ran the heavyweight adjacent oracle before timing.
Gate schema 2 now freezes 64K/128K ABBA controls, records pre/post load, one-second CPU, AC, and
thermal receipts, aborts on changed postflight state, and moves native parity plus the adjacent
oracle after all timing arms. The analyzer independently rejects invalid host receipts. This is
gate hardening only; no selector or Metal kernel behavior changed and selector 19 remains production.
### 2026-08-26: primary-source review isolates the next 128K decode axis
Apple's 2026 Metal Performance Primitives guidance says its most optimized GEMMs generally read
device memory directly and rely on sufficient occupancy for memory/compute overlap. That is useful
context, but it does not reopen Gem's direct-read attention axis: the locally measured Family-9
direct-cache variants already regress 31--35%, and an online-softmax attention kernel is not a pure
GEMM. Apple's advice to tune tile size against occupancy, edge quantization, and the number of GPU
cores instead reinforces the frozen BK64/BK128 one-axis gate.
FlashAttention-3 and FlashAttention-4 do not supply a portable M4 implementation shortcut. Their
main pipelines depend on NVIDIA Hopper/Blackwell asynchronous MMA, TMA, tensor memory, and
warp-specialized scheduling. Gem already screened the one algorithmic sub-idea that transfers
without that hardware--conditional online-softmax rescaling--and rejected it below the promotion
floor. No FA3/FA4 mechanism is admitted without an Apple-specific trace showing the corresponding
limiter.
The strongest applicable reference is Apple's current MLX two-pass vector GQA decode. Its first
pass divides a long KV sequence among independent threadgroups, carries exact online-softmax
partials, and has a duplication-free GQA variant that reuses each loaded K/V row across multiple
query heads. A second kernel combines per-block maxima, sums, and outputs. This matches Gem's
remaining post-split16 attribution: the P*V value side is already reduced, while the score/K pass is
the next long-decode ceiling. MLX's duplication-free specializations stop at symmetric D64 and
D128, while Gemma's global shape is symmetric `DK=DV=512`. Its algorithm and partial-state algebra
remain useful, but its register/threadgroup-memory geometry is not a drop-in D512 kernel.
After the BK128 prefill decision and final production acceptance gate, the next admitted diagnostic
is therefore a single fused split-KV GQA decode axis for only the ten 31B global layers. It should
hold F32 KV, exact online softmax, selector 19's model graph, and all server behavior fixed; sweep
only the number of sequence blocks at 64K and 128K. Required evidence is the existing threshold,
tail, deterministic-logit, authentic whole-token, memory, and adjacent backend-op gates. KV
quantization, sparse attention, paged layout, and M5/A19 TensorOps remain separate representation or
hardware axes.
Sources:
- [Apple Metal Performance Primitives Programming Guide](https://developer.apple.com/download/files/Metal-Performance-Primitives-Programming-Guide.pdf)
- [Apple GPU performance and Metal System Trace workflow](https://developer.apple.com/videos/play/tech-talks/111432/)
- [MLX Metal SDPA dispatcher](https://github.com/ml-explore/mlx/blob/main/mlx/backend/metal/scaled_dot_product_attention.cpp)
- [MLX vector and two-pass GQA attention kernels](https://github.com/ml-explore/mlx/blob/main/mlx/backend/metal/kernels/sdpa_vector.h)
- [FlashAttention-3 paper](https://arxiv.org/abs/2407.08608)
- [FlashAttention-4 paper](https://arxiv.org/abs/2603.05451)
### 2026-08-27: current MLX GQA source confirms split-KV shape, not direct reuse
A fresh primary-source pass found that MLX 0.30.4 explicitly highlighted a much faster Metal vector
GQA path for long context. Source inspected at
`d73eb752ef2e6288fd95b032c0bff0a15a4a9e93` makes the mechanism precise. For single-query GQA-8 with
at least 8K keys, the dispatcher launches one threadgroup per KV head and sequence block, with one
SIMD group per query head. The specialized first pass divides each block into contiguous token
subchunks, lets one SIMD group compute multiple query heads per loaded K/V subchunk, merges their
online-softmax state inside the threadgroup, and emits `(max,sum,output)` partials. A second kernel
reduces the sequence-block partials. The Apple-family heuristic increases the block count with
context, reaching 512 blocks at 64K and 1,024 beyond 64K on its current high-SIMD route.
This is stronger evidence for Gem's proposed fused split-KV design than the adjacent llama vector
path because it preserves GQA reuse. It still cannot be copied or treated as a performance oracle:
MLX admits the duplication-free path only at symmetric D64 and D128, whereas Gemma 4 global
attention is symmetric `DK=DV=512`. MLX's instantiations process eight heads per loaded row for D64
and four heads at a time for D128; extending its `G*HPT*V` threadgroup partial array to D512 with
multi-head reuse would exceed the M4 Max's 32 KiB threadgroup-memory limit. Gem should instead keep
one D512 accumulator per SIMD group, stage each K/V tile once for all eight groups, and sweep only
the sequence-split count after selector 20 is resolved. The source also validates a fail-closed
requirement already in Gem's plan: the partial count must be a SIMD-width-compatible value because
the reduction consumes 32 partials per chunk.
Apple's current profiler guidance also confirms that static threadgroup-memory limits are not an
occupancy measurement. An Xcode Metal trace must correlate occupancy with limiter and bandwidth
counters before choosing between larger K staging and more sequence splits. Apple Metal 4
cooperative tensors are useful for compute-bound matrix tiles, but the new neural-accelerator
benefit is specific to M5/A19-class GPUs; it does not justify changing the M4 Max decode path.
Operationally, `run-when-host-clean.sh` now waits for three consecutive load, one-second CPU, AC,
and thermal passes, logs every observation, and then `exec`s the unchanged gate. It never kills or
reprioritizes background processes. Deterministic fixtures prove streak reset, exact argument
preservation, timeout-without-launch, and receipt non-overwrite behavior. All 63 release and 63
ASan/UBSan tests pass after the subsequent traffic- and ceiling-analyzer integration.
The parked fused design's traffic premise is now mechanically derived rather than hand-recorded.
`analyze-decode-attention-traffic.sh` validates an exact-shape D512 profile and accounts for the raw
score write, three normalization passes, and one complete normalized-score read by each of the
sixteen dim32 output groups. Retained split16 profiles report 184,549,376 bytes at 64K and
369,098,752 bytes at 128K: 17.1875% additional logical traffic over the compulsory K+V reads. The
analyzer labels this minimum logical traffic, not device traffic, because Apple GPU caches may
satisfy some reads. It is non-promotional and explicitly requires Metal counters plus authentic
whole-token A/B evidence.
Sources:
- [MLX 0.30.4 release](https://github.com/ml-explore/mlx/releases/tag/v0.30.4)
- [MLX two-pass vector/GQA kernel, inspected commit](https://github.com/ml-explore/mlx/blob/d73eb752ef2e6288fd95b032c0bff0a15a4a9e93/mlx/backend/metal/kernels/sdpa_vector.h)
- [MLX SDPA dispatch and block heuristic, inspected commit](https://github.com/ml-explore/mlx/blob/d73eb752ef2e6288fd95b032c0bff0a15a4a9e93/mlx/backend/metal/scaled_dot_product_attention.cpp)
- [Apple occupancy guidance](https://developer.apple.com/documentation/xcode/finding-your-metal-apps-gpu-occupancy)
- [Apple 2026 custom Metal ML operations](https://developer.apple.com/videos/play/wwdc2026/330/)
### 2026-08-27: current-main recheck adds a routing-boundary correctness gate
MLX current `main` still selects its duplication-free GQA first pass only for single-query GQA-8,
no mask or sinks, symmetric D64/D128, and at least 8K keys. It also exposes a sequence-block
override rounded to a multiple of 32 because the second pass reduces partials in SIMD-width
chunks. These details reinforce Gem's proposed ownership and split-count constraints, but current
MLX still has no fused D512 route and therefore does not answer the register-pressure question for
Gemma 4 global attention.
A separate MLX report localizes deterministic bad generation to the exact point where a general
two-pass GQA route activates. The report is not evidence that Gem's proposed kernel shares that
bug, but it demonstrates that operator-level aggregate error alone is insufficient for a dispatch
boundary. The parked Gem gate now requires exact 8,191/8,192/8,193-token CPU and forced-selector-19
differentials plus matching greedy multi-token continuations. This is a validation refinement only;
no selector or Metal pipeline has been added.
Sources:
- [MLX current Metal SDPA dispatch](https://github.com/ml-explore/mlx/blob/main/mlx/backend/metal/scaled_dot_product_attention.cpp)
- [MLX issue 4022: two-pass GQA routing-boundary correctness failure](https://github.com/ml-explore/mlx/issues/4022)
- [MLX issue 3885: no fused D512 SDPA route](https://github.com/ml-explore/mlx/issues/3885)
### 2026-08-27: measured whole-token ceiling admits one D512 fusion diagnostic
The retained selector-19 authentic stage profiles and exact-shape split16 operator profiles now
feed a fail-closed ceiling analyzer rather than a hand calculation. Across the ten global layers,
the current D512 attention route accounts for 33.11% of whole-token GPU time at 64K and 43.73% at
128K. Its score stage alone accounts for 17.54% and 22.02%. A candidate must therefore reduce the
complete global-attention route by at least 9.06% at 64K and 6.86% at 128K to project a 3%
whole-token improvement.
This is enough residual to justify one future fused split-KV diagnostic after the earlier gates
close. It is not a speedup forecast: the candidate cannot eliminate compulsory K/V reads,
dot-products, exponentials, or the second-pass merge. `analyze-fused-split-kv-ceiling.sh` rejects
changed selectors, shapes, contexts, incomplete stage totals, weak sample coverage, and mismatched
model profiles, records all four input digests, and marks its result non-promotional.
### 2026-08-27: Paperbridge audit finds D512 feasibility evidence, not a long-context oracle
A Paperbridge search surfaced Open-TQ-Metal and BaseRT as recent Apple-native inference references.
BaseRT independently supports Gem's architecture choice--direct Metal, a zero-allocation decode
loop, preallocated KV, specialized GEMV/GEMM, and operator fusion--but its published evaluation uses
128--2,048-token prompts on M4 Pro and different Gemma variants. It supplies no dense Gemma 4 31B
64K/128K comparison and is therefore architecture corroboration rather than a benchmark oracle.
Open-TQ-Metal is closer to the proposed decode kernel. Source inspected at
`0f09466b7fde772a4876bf7bee3ccdeb34313304` instantiates D512 with 16 SIMD groups and 16 query plus
16 output floats per lane, showing that this accumulator shape compiles on Apple Silicon. However,
it launches one threadgroup per query head and maps that head back to its KV head, so global GQA-8
rereads shared K/V eight times. It then combines SIMD-group outputs through a serialized barrier
loop. Gem's planned one-threadgroup-per-KV-head ownership remains materially different.
The paper's Gemma performance table stops at 950 tokens, and the repository explicitly reports both
int4 and int8 KV failure beyond roughly 950/1,000 Gemma tokens. Its appendix also lists GQA factors
16 and 32, conflicting with Gem's authenticated GGUF topology of 2 for sliding layers and 8 for global
layers. Accordingly, neither its claimed Gemma speedups nor its KV quality generalize to this 64K/128K
goal. Quantized KV remains a separate rejected axis; only the D512 register shape and online-softmax
algebra inform the F32 candidate.
Sources:
- [Open-TQ-Metal paper](https://arxiv.org/abs/2604.16957)
- [Open-TQ-Metal Gemma source](https://github.com/mutable-state-inc/gemma4metal/blob/0f09466b7fde772a4876bf7bee3ccdeb34313304/lib/turboquant.metal)
- [Open-TQ-Metal Gemma limitations](https://github.com/mutable-state-inc/gemma4metal)
- [BaseRT paper](https://arxiv.org/abs/2607.00501)
- [BaseRT repository](https://github.com/basecompute/baseRT)
### 2026-08-27: Metal execution guidance narrows the host-overhead question
Apple's current guidance still treats command-buffer frequency as a CPU/GPU synchronization
tradeoff and recommends doing more work with fewer command buffers and encoders. Gem's authentic
decode path already submits one command buffer and one compute encoder per token, but records 1,393
dispatches inside it. The remaining wall-minus-GPU gap therefore cannot be attributed to command
buffer proliferation. It must be measured before changing ownership: likely contributors include
encoding 1,393 dispatches, driver scheduling and synchronization, readback, and the single small
`gem_metal_batch` control allocation. The allocation does contradict the roadmap's literal
"generation performs no heap allocation" wording, but removing it is not yet a justified speed
claim.
Apple's occupancy guidance likewise says that resource limits alone do not select a winning tile:
register, threadgroup-memory, and cache pressure can each cap useful occupancy, while excessive
occupancy can itself cause cache thrashing. The parked D512 fused split-KV diagnostic should
therefore collect an M4 Max Metal trace/heat map for its candidate and selector-19 baseline before
promoting a partition count. The 2026 Metal Performance Primitives tile advice is M5-specific and
does not override measured M4 behavior.
Sources:
- [Apple Metal command-buffer guidance](https://developer.apple.com/library/archive/documentation/3DDrawing/Conceptual/MTLBestPracticesGuide/CommandBuffers.html)
- [Apple Metal Performance Shaders tuning hints](https://developer.apple.com/documentation/metalperformanceshaders/tuning-hints)
- [Apple GPU occupancy guidance](https://developer.apple.com/documentation/xcode/finding-your-metal-apps-gpu-occupancy)
- [Apple GPU performance heat maps](https://developer.apple.com/documentation/xcode/analyzing-apple-gpu-performance-using-performance-heatmaps-a17-m3/)
### 2026-08-27: production-path timing rejects batch allocation as an optimization axis
`gem-decode-profile` now brackets the ordinary one-command-buffer/one-encoder execution separately
from its deliberately fragmented stage-counter pass. Profiling is opt-in, and the recorded prepare,
encode, submit/wait, cleanup, and logits-readback phases exactly partition the production wall time;
GPU execution remains nested inside submit/wait.
An initial 31B/F32 seeded-KV sample at position 8,192 was intentionally retained as dirty-host,
non-promotional attribution. Despite severe background interference making absolute GPU time nearly
an order of magnitude slower than earlier clean samples, GPU time was 99.8977% of wall time. The
complete wall-minus-GPU upper bound was 1.012 ms, while cleanup containing the batch `free` averaged
0.0053 ms. Removing the small control allocation therefore cannot plausibly clear the project's 3%
whole-token floor. Kernel GPU time remains the correct optimization target. A clean-host 64K/128K
sample may confirm the ratio, but is not required before rejecting allocation removal as a speed
axis.
Evidence: `artifacts/2026-08-27-decode-host-overhead/seeded-production-8192-dirty-host.json`.
### 2026-08-27: fused split-KV CPU merge oracle landed without a Metal route
The parked D512 diagnostic now has an engine-owned CPU reference for online-softmax partial
construction, exact pairwise merge, final normalization, and bounded scratch sizing. Tests cover
non-power-of-two token and value dimensions, uneven tails, more partitions than tokens, empty
partials, non-finite rejection, invalid empty-state encoding, and size overflow. The fixed P=32,
QH=32, DV=512 layout is 2,105,344 bytes including maxima and denominators. No selector, Metal
pipeline, workspace reservation, or production behavior was added; the earlier experiment ordering
remains intact.
The independent `gem-online-merge-check` harness subsequently exercised the fixed D512/P32 algebra
at the exact future routing boundary (8,191, 8,192, and 8,193 tokens) and the 65,536/131,072-token
tails. All five cases were finite and bitwise repeat-deterministic with correlation at least
`0.999999999999`, NMSE no greater than `1.42e-11`, and maximum absolute error below `1.64e-7`
against a separate double-precision direct softmax. The digested audit is
`artifacts/2026-08-27-fused-split-kv-cpu-boundary/audit.json`. This does not cover a Metal kernel,
full-model logits, or greedy continuation.
The D512 compiled-resource schema is now fail-closed before a kernel exists. Its analyzer rejects
changed tile/P/D/KV geometry, incorrect partial scratch, non-M4 device limits, an unlaunchable
pipeline, changed dynamic threadgroup memory, and missing source/binary identities. The fixed first
pass is 256 threads/eight SIMD groups and 16,384 dynamic bytes; the reducer is 256 threads and zero
dynamic bytes. No fabricated production receipt was recorded—the checked fixture establishes only
the future artifact contract, and an authentic receipt still requires compiled Metal pipelines.
The next correctness receipt is now equally constrained. Its analyzer consumes the existing CPU
gate, future compiled-resource analysis, five raw D512 operator differentials, and three raw
evaluated-prompt 31B model differentials. It rejects boundary/long-tail changes, route or selector
drift, non-finite or nondeterministic output, weak correlation/NMSE, changed model identities,
argmax/top-10 mismatch, fewer than eight continuation tokens, or any greedy-token divergence. Test
fixtures prove those failure modes without fabricating an authentic candidate result. No Metal
kernel, correctness claim, or promotion receipt exists yet.
### 2026-08-27: MLX 0.32.2 independently validates shared-K/V GQA ownership
MLX 0.32.2, released on August 25, includes PR 4077's specialized GQA-8 two-pass decode kernel.
The first pass assigns contiguous token subchunks to SIMD groups, keeps multiple query heads in
registers for each shared K/V stream, and merges their online-softmax partials through threadgroup
memory. On the submitted M5 Pro measurements, this improved the isolated 32-query/4-KV-head D128
kernel by 1.07x, 1.09x, and 1.14x at 8K, 16K, and 32K. Independent M5 Max verification reported
1.16x, 1.08x, and 1.12x, with a 7.0% full-model decode gain at approximately 32K. These are not M4
Max or D512 performance claims, but they independently validate that avoiding redundant GQA K/V
loads can survive wider Apple silicon and whole-model execution.
The implementation also sharpens Gem's resource constraint. MLX selects eight heads per SIMD group
at D64 but only four at D128 because the larger accumulator spills at eight. It deliberately stops
at D128 and leaves D512 unsupported. Gem therefore must not transplant that per-SIMD multi-head
geometry. The parked candidate's one D512 query/output accumulator per SIMD group, eight SIMD
groups per KV head, tile4 16 KiB shared K/V staging, and mandatory compiled-resource/occupancy
receipt remain the defensible first experiment. The new release adds evidence for the ownership
choice; it does not reorder the BK128, production-128K, selector-20, and fused-D512 gates.
Sources:
- [MLX 0.32.2 release](https://github.com/ml-explore/mlx/releases/tag/v0.32.2)
- [MLX PR 4077: read each K/V byte once in GQA-8 decode](https://github.com/ml-explore/mlx/pull/4077)
- [MLX current Metal SDPA dispatcher](https://github.com/ml-explore/mlx/blob/main/mlx/backend/metal/scaled_dot_product_attention.cpp)
- [Apple threadgroup sizing guidance](https://developer.apple.com/documentation/metal/calculating-threadgroup-and-grid-sizes)