Skip to content

perf(GFX1100-TG200): raise Qwen3.5-4B Q4_K_M gfx1100 decode to 76.6 tok/s - #6

Open
ghazni101 wants to merge 98 commits into
mainfrom
row/GFX1100-TG200
Open

perf(GFX1100-TG200): raise Qwen3.5-4B Q4_K_M gfx1100 decode to 76.6 tok/s#6
ghazni101 wants to merge 98 commits into
mainfrom
row/GFX1100-TG200

Conversation

@ghazni101

@ghazni101 ghazni101 commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Row

GFX1100-TG200 — raise Qwen3.5-4B Q4_K_M single-stream decode throughput on RX 7900 XTX (gfx1100) toward >=200 tok/s pure-autoregressive greedy. Spec: .agents/specs/gfx1100-tg200.md. Tracking issue: #5.

Before starting

What changed

Measured position moved 40.65 -> 76.60 tok/s median (+89%; the pre-campaign position was 27.6) on the acceptance workload: canonical 109-token prompt, 256 generated tokens, greedy, batch 1, idle host, gpu-ctl lock held. Levers, in landing order:

  • T2b: ROCm static-shape decode-graph capture engaged (support_static_graph_mode) — wall/token approaches GPU-busy/token.
  • T3a: f32-query DecodeGqa attention arm (TG150 carryover, LDS repair).
  • T4a: K-quant decode GEMV arm VT_GEMV_MMVQ=1 (+13.2%, byte-identical outputs) plus the m-gated dispatch and routing-witness gates; lever-B1 fold crossover tunable behind VT_GEMV_MMVQ_FOLD_MAX; lever-B2 f32-out skinny-BF16 arm VT_SKINNY_BF16=1 (+8.7%) displacing starved hipBLASLt Cijk tiles.
  • Lever C: fused Q8_K activation-quant epilogue in RmsNormRowKernel behind VT_NORM_QUANT_FUSED=1, producer-token consumption in the MatmulBTQuant dispatch; kills standalone single-block quant launches with byte equality by construction.
  • T5a: the shared QuantQ8KSBlock body vectorized (dtype unswitched once, 16-byte loads, amax scan kept in strict ascending element order so the first-occurrence lowest-index tie-break stays bit-exact) — +23% alone; every consumer (standalone quantizer, fused epilogue, MMVQ prologue) benefits.
  • T5b: PagedAttnDecodeGqaF32Q instantiated at head_dim 128 behind VT_ATTN_DECODE_GQA4=1 — f32-query full-attention decode had fallen to the generic serial kernel at 276us/call.
  • T6a: warp-per-row cooperative GDN scan behind VT_GDN_SCAN_COOP=1 (donor walk was uncoalesced, ~17 GB/s effective).
  • T6b: warp-per-item cooperative attention qk-norm/RoPE/gate preamble behind VT_ATTN_PREAMBLE_COOP=1.
  • Closed NEGATIVE and reverted: pointer-keyed quant cache (allocator recycling breaks it), T4a round-1 unfused-fold GEMV (garbage-fast number-loop degeneration), lever-C on the 4B vehicle (see gaps), T5c nontemporal weight loads in the MMVQ body (wash).

All seven knobs are classified kernel-internal on scripts/env-doc-allowlist.txt per docs/ENVIRONMENT.md's own policy; none flips a default.

Evidence

  • Acceptance A/Bs: interleaved same-window pairs, medians never best-case, in docs/bench-evidence/gfx1100-tg200-t1..t4a-2026*.md and gfx1100-tg200-t5-native-baseline-20260825.md. Closing table: GPU busy 12.13 ms/tok of ~13.1 wall (dispatch gap ~1 ms after graph capture).
  • Bit-exact levers claim engine-level BYTE-IDENTITY (T4a: md5 both arms x5 reps; T5a/T5b: cmp over completion bodies vs pre-change builds). Order-changing levers (T6a/T6b, lever C) are NMSE-gated ops with per-A/B near-tie adjudication records: tie flips at char 204/285 of the gate prompt, coherent prose both sides.
  • Op gates: tests/vt/test_rocm_quant_dot 12/12 cases 797 assertions including tied-amax adversarial; ctest -R 'rocm|cross_device|quant' green except two failures proven PRE-EXISTING by rerun on the pristine head (test_gguf_keep_quant, one cross_device case — owned separately).
  • python3 scripts/agent-ready.py: All gates green on this tree (native gfx1100 build, -DVLLM_CPP_HIP_ARCHITECTURES=gfx1100).

Speed claims

  • This PR makes NO speed claim
  • The operator ran the numbers under the gpu-ctl lock (gpu-ctl run/reserve, standing serve parked via reservation) and they are recorded with repro recipes in docs/bench-evidence/gfx1100-tg200-*.md; gate scripts tools/tg200-run-gate{,2}.sh reproduce every window.

Honest gaps

  • 76.60 tok/s against the 200 target: the gap is OPEN. Next order is named in the session-close attribution table: GemvMmvq geometry rewrite (top tractable, ~3.7 ms/tok family; load-policy tweak already closed negative), RmsNorm fused-epilogue residue (~18us x 65/tok), lm_head-class KQuantGemmK arms (~1.2 ms/tok). No ceiling declared anywhere.
  • Teacher-forced logprob-band ceremony (per .agents/specs/rocm-m4-oracle.md) is OWED before any default flip of VT_ATTN_DECODE_GQA4, VT_GDN_SCAN_COOP, or VT_ATTN_PREAMBLE_COOP; until then they ride the campaign config and stay default-OFF.
  • Lever C's +7.3% adoption measurement was taken on the 0.8B smoke vehicle; on the 4B acceptance workload it measures neutral (50.72 OFF vs 49.89 ON pre-T5a) and is recorded honestly; it stays opt-in.
  • docs/BENCHMARKS.md / docs/USAGE.md rows ride the T6 landing stage, which fires when the gate is met or the campaign reports its final measured position.
  • Two pre-existing native-build gate failures (test_gguf_keep_quant, one test_backend_cross_device case) reproduce on the pristine head without this branch's changes; they are this configuration's debt, not this campaign's regression.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]

The ROCm backend registers no quantized-weight GEMM provider, so every
GGUF k-quant weight on an AMD card computes off device. The spec scopes
W1 as kMatmulBTQuant and kMatmulBTQuantGrouped providers mirroring the
CUDA sibling, which the GGUF loader reaches automatically once they are
registered, and owes the upstream csrc/rocm W4A16 family behind a loader
consumer that does not exist yet. The issue index gains the three
2026-08-21 campaign issues: mudler#1586 toolchain adoption and optimization,
mudler#1587 this row, mudler#1588 the Qwen3.5-0.8B numerics characterization.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
… kROCM

The GGUF loader routes a block-typed weight to MatmulBTQuant whenever the
running device has the provider, so registering these two ops lights up
keep-quant compute on every ROCm board with no model-path change: the
dense and grouped MoE towers stage once through ResidentWeight and
dispatch to the new device GEMM.

Coverage mirrors the CUDA sibling exactly — the ten Q8_K-family
encodings plus a native Q8_0 arm. The integer dots are the portable
scalar forms of the CPU reference bodies in the CPU accumulation order,
because gfx1100 exposes no signed byte dot (v_dot4_i32_iu8 is
unsigned-only; sdot4 needs a feature this target does not offer), and
the gate is bit-exactness against the CPU tier at NMSE 1e-6 with the f64
dequant band at 5e-4. Unsupported dtypes throw naming the dtype instead
of silently falling back to a host kernel that cannot follow device
pointers; VT_GGUF_KEEP_QUANT=0 restores load-time expansion.

Gates on gfx1100 / ROCm 7.14.0: test_rocm_quant_dot 132,094 assertions
green across all ten encodings (decode through prefill shapes, broadcast
and per-row grouped arms over a poisoned output buffer), focused
ctest 'rocm|cross_device|quant' 20/21 with only the pre-existing
MoeSiluMul bf16 exactness failure (mudler#1588) remaining, and an end-to-end
Qwen3.5-0.8B Q4_K_M decode that is deterministic on device.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
QuantDotGemm is 48.3% of GPU busy in the 97,721-dispatch rocprofv3
capture of Qwen3.5-4B Q4_K_M decode at 6236e9e, and it streams
weights one byte per lane, so the model decodes at ~163 GB/s effective
weight streaming, about 17% of the RX 7900 XTX peak. The spec scopes an
attempt ladder that vectorizes the weight loads to 16 bytes per lane
and reshapes the wave geometry without touching the accumulation order,
because test_rocm_quant_dot pins the integer core bit-exact against the
CPU reference and stays unchanged. The issue index gains the campaign
row under mudler#1586.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
The keep-quant GEMM assigned one super-block per lane (sb = lane), so a
decode-shape row with nsb = k/256 in {10..36} idled 22 of 32 lanes on
every block and serialized each output on one lane. The rocprofv3
capture priced the result at 392.8 us for a [1,2560]x[9216] Q4_K GEMM
(13.3 MB of weights, ~34 GB/s) and 1438.5 us for the [1,9216]x[2560]
Q6_K arm (~13 GB/s).

The tail path now splits every super-block across all 32 lanes (8
elements per lane, DotSuperblockWarp) and warp-reduces the integer
partials; whole super-blocks beyond the first multiple of 32 keep the
scalar path. Two's-complement int32 addition is associative, so the
lane split of the reference element order is bit-exact, and the float
scales still apply only after the integer sums. The dmin/bsums
correction terms apply once per block on lane 0. The IQ families keep
the scalar body inside the warp variant (grid lookups dominate there,
and their nsb is large), and DotQ2K/DotQ4K/DotQ5K lose their int8_t
aux8[256] stacks to fused decode, dropping QuantDotGemmKernel<Q4_K>
from VGPR 192 to 64.

test_rocm_quant_dot: 132094 assertions green, unchanged. Steady decode
of Qwen3.5-4B Q4_K_M on gfx1100 drops from 8.16 ms GPU-busy per token
(~336 GB/s effective weight streaming) to 4.41 ms (~622 GB/s, 65% of
peak); warm wall clock goes from 9.8 to 17.8 tok/s.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
The developer set a goal on 2026-08-22: serve Qwen3.5-4B Q4_K_M on the RX
7900 XTX at 150 tok/s text generation, pure autoregressive greedy decode
with no speculative path, on an fp8-e4m3 KV cache. Issue mudler#1651 records the
goal and this spec turns it into a gated, staged campaign: S1 re-prices the
attribution on the exact acceptance workload before any lever is chosen,
S2-S5 take the kernel and dispatch levers in S1's order, S6 extends the
landed KV-FP8 CPU brick to a ROCm store and paged-attention read behind the
cache_dtype=fp8 surface that rocm_paged_attn.hip refuses by name today, and
S7 runs the median-of-five idle-host acceptance gate. The index gains the
campaign row; mudler#1586 stays scoped to the quant-GEMM bandwidth ladder.

The wall-vs-GPU-busy gap at current head (17.8 tok/s wall against 4.41 ms
GPU busy per token) is recorded as an inference to verify, not a finding:
one rocprofv3 capture at a different prompt length priced it, and S1 owns
the re-take.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…ttn arm

The S1 rocprofv3 re-take on the acceptance workload (512-token prompt, 256
generated tokens, gfx1100) attributed 24.1 of the 54.8 ms/token decode budget
to QuantDotGemmKernel<Q6_K> at n=2560/K=9216: 16 calls per token at ~1507us
each, streaming the 19.35 MB of weights at 12.8 GB/s against the ~296 GB/s
its Q4_K sibling achieves. The hot path for nsb >= 32 shapes is the SCALAR
DotQ6K — with full_sbs = 32, each lane decodes one whole super-block through
the old switch-on-quadrant body with size_t address arithmetic per element.
A standalone HIP microbench proved the dot bodies are equivalent in
isolation; the cost is the serial per-element decode on the lane-split path.

DotQ6K is restructured to unroll the four quadrants per 128-chunk with
pure-int addressing and no switch: same element order, same scale-per-group
application point, so the integer core stays bit-exact
(test_rocm_quant_dot 132,094 assertions unchanged). Measured on the gate
workload: ffn_down falls to ~509us/call and end-to-end warm tg rises from a
13.1 to an 18.5 tok/s median (+41%). A microbench of the new body shows it
at parity or better versus both old forms.

Also lands the first S2 attention lever behind VT_ATTN_DECODE_GQA4=1
(default OFF): PagedAttnDecodeGqaBf16<4,8,8> for the qg==4/d==256 geometry,
which currently falls to the generic PagedAttnOnline (818us/call, 6.1
ms/token across the 8 full-attention layers). NOT YET EFFECTIVE for this
model: the GGUF dense path passes an f32 query while DecodeGqa is bf16-only,
so the arm is recorded as owed the f32-query extension plus the near-tie
adjudication before any default flip. The focused cross_device gate keeps
its documented pre-existing MoeSiluMul bf16 failure (mudler#1586/mudler#1588); all other
cases green.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
The S2 budget after the branch-free Q6K fix still showed ffn_down at ~510us
per call: 2560 warps each serially walking 36 super-blocks is latency-bound,
not bandwidth-bound — lm_head proves the memory system delivers 598 GB/s on
the same kernel family. This adds a split-K variant for decode shapes
(m == 1, nsb >= 8, n >= 512, non-f32 out): K is divided across 4 warps per
output, each warp writes its scaled float partial to a dedicated buffer, and
a small reduce kernel folds the partials and converts to bf16. The integer
core is untouched; only the float scale-sum reassociates, inside the NMSE
band the file's gate already accepts.

Two defects caught during bring-up, both by end-to-end A/B rather than the
op-level gate: the first A/B showed no delta because the arm gated on f32
out while every model-path GEMM emits bf16; the second produced garbage
tokens because the partials buffer was taken from EnsureScratch — the same
per-stream buffer that backs the quantized activation, so the split-K kernel
clobbered the very activations it was reading. Partials now live in their
own grow-only allocation.

Measured on the acceptance workload (512-token prompt, 256 generated,
greedy, idle box, gpu-coord lock held): 18.5 -> 22.7 tok/s median (+23%).
Cumulative campaign position: 13.1 -> 22.7 tok/s (+73%) today. Output text
matches the plain path token-for-token for roughly the first 40 tokens and
then drifts — the expected float-reassociation cascade in greedy decode;
the op gate stays green (132,094 assertions) and the near-tie adjudication
before any default-flip decision is recorded as owed.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…A4=1

The S2 re-profile at the split-K head showed PagedAttnOnline as the top
remaining lever: 6.05 ms/token across the model's 8 full-attention layers,
803us per call. The generic kernel handles one (token, head) pair per CTA
and walks the KV sequence with a block-wide __syncthreads() online softmax
per context token — a sync storm at decode shapes, with 16 CTAs on a 96-CU
part.

The DecodeGqa geometry fixes both: QG q-heads fused into one CTA read each
K/V row once, and the walk is warp-strided with only end-of-loop warp
reduces. The existing DecodeGqaBf16 kernel is bf16-in/bf16-out, while this
model's GGUF dense path runs "Phase 1" attention numerics — an f32 query
and f32 output over the bf16 KV cache — so it never qualified. This adds
the mechanical sibling: PagedAttnDecodeGqaF32Q<QG,EPL,NWARPS> (f32 query,
bf16 K/V, f32 out) via templated LoadRowEplF32/StoreRowEplF32 helpers, plus
a dispatch arm gated on VT_ATTN_DECODE_GQA4=1 for the exact dtype/geometry
combination (f32 query/out, bf16 cache, d=256, hq=16/kv=4).

Measured on the acceptance workload: attention calls drop from ~803us to
sub-100us; end-to-end rises from a 22.7 to a 25.7 tok/s median (+13%).
Cumulative campaign position: 13.1 -> 25.7 tok/s (+96%) today. Output is
coherent text; the reduction-order change means greedy anchors can move at
exact ties versus the generic path, same policy class as the d128 arm —
the near-tie adjudication before any default flip stays owed.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…GEMV

Two levers from the decode kernel-sequence dump at the split-K head.

KEEP-QUANT UNDER THE V-ROW REORDER (qwen3_5_gguf_weights.cpp). The GDN
in_proj_qkv/attn_gate weights are Q5_K, but the V-head row reorder made them
kTransformedWeight, forcing bf16 expansion at load and hipBLASLt decode
GEMMs. A row permutation cannot cut a ggml K-block — each row is whole
blocks — so OwnGgufQuantBlocksRowPermuted now applies the reorder to the
quantized rows directly. 48 tensors stay packed (~0.9 GB less host+device
memory); the resident weight encodes exactly what dequantize→reorder→bf16
encoded. End-to-end neutral on throughput: these GEMMs were already
bandwidth-fine on hipBLASLt; the win is memory and quant-path coverage.

TINY-N DECODE GEMV (rocm_matmul_hipblaslt.hip). The per-layer kernel
sequence showed two hipBLASLt calls plus epilogues between in_proj and the
GDN conv — the ssm_alpha/ssm_beta projections (N=32, K=2560), ~78us of
fixed library launch cost for a 164 KB weight read, ~4.1 ms/token across 24
layers when both their bf16-out and f32-out forms are counted. Both forms
now route to a row-per-block GEMV (bf16-out via the existing Bf16GemvBT,
f32-out through a new GemvBTF32OutKernel templated on the activation type),
forced for N<=64 independent of VT_ROCM_GEMV. Measured on the acceptance
workload with the attention arm enabled: 25.0 -> 27.6 tok/s median (+10%).
Cumulative campaign position: 13.1 -> 27.6 tok/s today (+110%).

Also adds an env-gated VT_MM_TRACE shape log to MatmulBTKernelRocm that made
the caller attribution possible; it stays as a diagnostic.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
The developer set a goal on 2026-08-22: serve Qwen3.5-4B Q4_K_M on the RX
7900 XTX at 200 tok/s single-stream text generation, pure autoregressive
greedy decode, no speculative path. Issue #5 (ghazni101/vllm.cpp) records
the goal and this spec turns it into a gated, staged campaign. Feasibility
is recorded as settled -- llama.cpp sustains ~200 tok/s on this exact
checkpoint and GPU with a q8 KV cache, our own lm_head streams 598 GB/s on
this board, and the ceiling arithmetic puts the target at ~47% of peak --
so no stage relitigates it.

The base is pinned at upstream tip 019f66c rather than the TG150-era
base, because main has since landed three levers inside exactly the budget
TG150 measured remaining (GdnPostConvK single-thread value_dim copy,
VT_ATTN_DECODE_D128 default-on for ROCm, wvSplitK decode-skinny GEMM
routing); T1 exists to re-price the tip on the exact acceptance workload
before any new lever is chosen. Stages T1-T6: attribution re-take,
dispatch-collapse (HIP graph / FusedChain), GDN family decode levers,
residual quant-GEMM arms continuing mudler#1586's ladder, hipBLASLt/wvSplitK
arms, acceptance gate + landing. The index gains the campaign row;
mudler#1586's scope is unchanged.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…usy 8.64 + gap 2.08 ms/tok

T1a wall clock on the exact acceptance workload at base 019f66c: runs
40.639/40.671/40.712/40.594 tok/s after one warmup, median 40.65 tok/s.
The pre-merge position of 27.6 tok/s is stale: the three upstream levers
that landed inside this budget (GdnPostConvK value_dim fix, VT_ATTN_DECODE_D128
default-on, wvSplitK skinny GEMMs) bought about +13 tok/s before any new
campaign work.

T1b rocprofv3 capture, steady-state window over 511 tokens: wall
10.72 ms/token = GPU busy 8.64 ms + host dispatch gap 2.08 ms (inter-
dispatch idle). Top items: the SECOND GdnPostConvK instantiation still
runs grid=1-block at 183 us per call, 10.8 calls/token = 1.98 ms/tok --
the f4ccabb repair covered the K-variant single-thread copy and this
sibling kept the pathology; Q6K QuantDotGemmKernel 1.24 ms/tok; bf16
PagedAttnOnline 1.07 ms/tok at grid=1 with a block-wide sync per context
token; hipBLASLt Cijk 0.70; GdnScan 0.51; one straggler SplitK launch
(grid=124160) 0.44.

Target arithmetic recorded in the evidence file: 200 tok/s = 5.00 ms/tok,
so the campaign needs busy ~8.64 -> ~3.2 AND gap ~2.08 -> ~0.5. The lever
order T1 outputs is: second GdnPostConvK geometry, HIP-graph dispatch
collapse, PagedAttnOnline coverage, then Q6K quant-GEMM bandwidth.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…=1 pathology was a profiler artifact

T1b's budget table priced GdnPostConvK at grid=1, 183 us per launch,
1.98 ms/token -- the largest GPU item. That row was an artifact: the GDN
decode step dispatches kGdnPostConv twice per layer with two different
host-side argument tuples, and both collapsed into ONE kernel symbol in
the rocprofv3 capture, averaging their durations into a misleading entry.

A measurement build (NOT landed) registered a byte-for-byte duplicate TU
of rocm_gdn_postconv.hip with renamed symbols as a second provider named
"vt-native2" for (kGdnPostConv, kROCM), which wins the priority-0 tie by
name order and routes every dispatch through it. RegisterOpProvider
rejects duplicate provider names, so a second "vt-native" registration is
not an option; the duplicate must carry its own name. With symbols
separated the same work reads 10.8 launches/token at ~28 us = 0.30
ms/token: the "grid=1 pathology" was the collision's average, not a real
defect. The measurement build stays out of the tree -- landing it would
touch CMakeLists.txt (a user_usage/landing_page file) and owe a
docs/USAGE.md edit for zero user-visible change.

Wall clock was throughput-neutral on three 5-rep sets under co-tenancy:
33.3 / 33.07 / 33.22 tok/s median (host load 12-20; two earlier attempts
died with hipMalloc OOM when a co-tenant model was resident) against the
T1a idle-host baseline of 40.65. The re-taken capture's top item is now
QuantizeQ8KK: 129 launches/token at ~59 us, grid=128 each -- about 16K
super-blocks quantized per launch where the decode shapes (m=1, K=2560 ->
nsb=10) need grid=1. That ~3.4 ms/token of activation quantization is the
next lever, not postconv.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…ured budget

Two-source study: vLLM via web (subagent), SGLang via a local shallow clone
read directly (sglang-src at the shared checkout root; kernels under
python/sglang/kernels/aot/csrc/quantization/gguf/). Findings, ranked by
expected ms/token recovered on our T2a capture:

1. SGLang GGUF MMVQ (fused_mul_mat_gguf -> ggml_mul_mat_vec_a8, mmvq.cuh):
   for batch <= mmvq_safe the activation is quantized ONCE to q8_1 by a
   single small kernel (quantize_row_q8_1_cuda, one warp per 512-padded
   row, wave reduce), then mul_mat_vec_q runs ONE WARP PER OUTPUT ROW
   with q4_K/q5_K/q6_K blocks dequantized in registers (vec_dot_*_q8_1).
   Our engine instead runs 129 QuantizeQ8KK launches/token at grid=128
   (~16K super-blocks each, ~59 us) = ~3.4 ms/token of activation
   quantization where the decode shapes (m=1, K=2560 -> nsb=10) need
   grid=1. Porting the MMVQ geometry to rocm_grouped_gemm.hip is the top
   lever; it also removes the Q8_K scratch round-trip. The q8_1 vs Q8_K
   convention changes reduction order, so the arm lands behind the
   ratified near-tie adjudication, not a bit-exactness claim.

2. vLLM W4A16 (gptq_marlin/triton): activations stay bf16; weight
   dequant happens inside the GEMM registers. Same destination as (1).

3. Graph capture: both engines capture the whole uniform-decode step as
   ONE replay (vLLM FULL_AND_PIECEWISE; SGLang "full" backend default).
   Our seam exists (BACKEND-ROCM W1 hipGraph + BreakableGraph;
   platforms/rocm.cpp keeps support_static_graph_mode false pending the
   model-path W2). Flipping it for this model attacks the 2.08 ms/token
   dispatch gap.

4. Merged gate_up: vLLM MergedColumnParallelLinear packs gate+up into
   one GEMM. Our bf16 arm already has gate_up_proj; the GGUF keep-quant
   arm loads separate gate/up owners and runs two quant GEMMs. Merging
   halves those sites (rides layers::MlpGateUpMethodBase).

5. RDNA3: no first-party gfx1100 tuning upstream (AMD CI targets CDNA);
   autotune locally. LDS 64KB/workgroup cap; no MFMA (WMMA only);
   hipBLASLt Cijk tiles are large-batch tuned -- at m=1 a custom
   N-major skinny GEMM usually beats them.

Checkpoint facts verified by direct GGUF header parse: Qwen3.5-4B H=2560,
32 layers (24 GDN + 8 full-attn), ffn 9216; every GEMM weight is
Q4_K/Q5_K/Q6_K keep-quant (attn_qkv + ssm_out Q5_K, rest Q4_K/Q6_K mix),
so all 129 quant launches/token come from the keep-quant GEMM path.

Record-only: no production code changes in this commit.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…tor recycling breaks it

Follow-up to the T2a instrument repair. The 129 QuantizeQ8KK launches per
token looked like pure launch-overhead waste, so a per-stream cache keyed on
the activation pointer was tried: first GEMM over an activation quantizes,
later same-key GEMMs reuse the scratch.

Two cuts, both REJECTED for correctness:

- Pointer-only key: throughput rose to ~45 tok/s median but output
  degenerated into repeated garbage. Root cause: the DevicePool recycles
  activation blocks ACROSS steps -- the same pointer carried different
  content and stale quantized data was served.
- Epoch keying (vt::BumpQuantEpoch/CurrentQuantEpoch bumped once per model
  forward): still degenerate. Within ONE step the pool hands the SAME
  address to DIFFERENT activations (a DBuf is freed and its block
  re-allocated mid-forward), so pointer identity does not imply content
  identity even inside a single step.

Both cuts reverted before landing; revert verified by coherent acceptance-
workload output. Evidence file records both cuts with numbers.

What this fixes in the campaign's aim, not just documents: the sound levers
for that budget are structural -- merged gate+up keep-quant GEMM (halves the
sites), MMVQ-style dequant-in-register decode GEMV following SGLang's
mmvq.cuh (removes the separate quant kernel entirely), and ROCm decode-graph
capture (removes the ~59us/launch queue overhead that makes each tiny
kernel expensive). The probe instrumentation also stays out; it served its
purpose.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…MLP instead of two

The T2a probe census showed the dense SwiGLU MLP issuing TWO kMatmulBTQuant
calls per layer (gate, then up) over the SAME activation, each paying its own
QuantizeQ8KK launch. Following vLLM's MergedColumnParallelLinear topology,
this loads ffn_gate + ffn_up rows-stacked into ONE [2I, H] nk=true block owner
and runs ONE quant GEMM into [T, 2I], feeding the unchanged SiluAndMul + down.

Byte-exactness: each output row's integer dot runs over its own whole K-
blocks; stacking only concatenates output rows, so every row's result is
identical to the split call (verified: coherent acceptance-workload output,
token stream matching the pre-change build's greedy run).

Implementation:
- OwnGgufKeptStacked (gguf_weights.cpp): copies both keep-quant halves into
  one contiguous owned buffer; two disjoint file spans cannot be borrowed as
  one mapping span, so this arm always copies.
- LoadMergedKeptGateUp: returns empty when encodings or K differ -- caller
  falls back to the split pair.
- DenseMlpBlock: the existing gate_up_proj branch now serves both the bf16
  and the stacked keep-quant owners; dispatch keys on emptiness as before.

A/B on the acceptance workload: both arms ~35.8 tok/s median in a co-tenancy
window (the same-window split baseline read 32.2-35.9). No regression; the
win is one fewer QuantizeQ8KK + one fewer GEMM launch per layer per token,
which compounds with the decode-graph stage where per-launch cost dominates.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…de-graph capture engaged

The last false predicate in the dense decode-graph gate chain falls:
support_static_graph_mode() returns true on ROCm. With W1's hipGraph
capture/replay seam already implemented (rocm_backend.hip, with the
mutate-src-then-replay assertion) and the keep-quant scratch pool already
capture-safe (hipMallocAsync, stream-ordered, never freed), the flip engages
Qwen3_5DenseDecodeGraph for uniform decode steps.

Verified live: "[DenseDecodeGraph] captured ... padded size S=1" then "14
total replays across 1 captured size(s)" on a 16-token run; output coherent.

A/B (acceptance workload, 256 tok x 5): 36.4 tok/s median in a co-tenancy
window vs 35.8 same-window split-arm baseline -- neutral-to-slightly-positive
under contention, as expected since a shared GPU queue partially hides the
dispatch gap. The full ~2.08 ms/tok gap removal shows only in an idle-host
window (projected ~46+ from the 40.65 baseline); the definitive idle-host
capture is queued as campaign follow-up.

Evidence: docs/bench-evidence/gfx1100-tg200-t2b-20260823.md

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…indsight 500s)

Records-only: appends the T2b session's hindsight note to
docs/bench-evidence/gfx1100-tg200-t2b-20260823.md so the next session
resumes from the recorded position instead of re-deriving it. No source,
test, or measurement change.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
… -- and fixes its LDS underallocation

PagedAttnOnline<f32,bf16,f32> is the top GPU item at this head (8 calls per
token, grid=1-block, one __syncthreads() online-softmax step PER context
token -- a serial walk measuring ~593us/call in the T2c capture). The model's
full-attn layers run f32 query x bf16 cache x f32 out, which excludes every
bf16 decode kernel, and FA2 stays CUDA-only on ROCm. This ports
PagedAttnDecodeGqaF32Q<4,8,8> from c112d88 on the TG150 ladder: QG=4 fused
q-heads per KV group read each K/V row once, warp-strided sequence walk,
register online softmax, no per-key block sync. Dispatch arm gated behind
VT_ATTN_DECODE_GQA4=1 for the exact geometry (f32 q/out, bf16 k/v, d=256,
hq=16/kv=4) so the default path is unchanged.

The port landed with the TG150 dispatch's shared-memory formula --
nwarps*2*d floats -- but this kernel's layout needs nwarps*QG*d; with QG=4
the arm ran on HALF the required LDS. The op-level suite stayed green (its
GPU-parity cases are HasCuda()-guarded and skip on ROCm-only boxes) while
the engine degenerated into number-loops after ~20 tokens at an apparent
39.5 tok/s. Caught by the token-coherence check on the acceptance workload;
the fix keys the smem formula on QG. The garbage-fast lesson is recorded in
the evidence file: a throughput median without an engine-level coherence
check would have shipped it.

After the fix: coherent output, full 256-token streams differ only in
mid-stream near-tie moves (reduction order changes vs PagedAttnOnline, same
policy class as VT_ATTN_DECODE_D128). Clean-window interleaved A/B under
co-tenant load noise: ON ~36.8 vs OFF ~36.2 tok/s median; the definitive
idle-host capture is owed once the co-tenant compile storm clears. Near-tie
adjudication before any default-ON flip remains owed; the arm ships opt-in.

Evidence: docs/bench-evidence/gfx1100-tg200-t3a-20260823.md

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
Records-only: appends the T3a session's hindsight note to the t3a
evidence file so the next session resumes from the measured position.
No source, test, or measurement change.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
The campaign spec names tests/vt/test_rocm_quant_dot.cpp as the quant-path
lever gate, but that file never existed: every GPU-parity keep-quant case
lives in test_cuda_quant_dot.cpp behind HasCuda(), so on this ROCm-only box
the whole op-level suite silently skipped (the exact T3a blind spot). This
creates the focused ROCm kMatmulBTQuant gate, guarded on ROCM availability,
never on CUDA, registered under VLLM_CPP_HIP next to test_rocm_backend.

The gate is written RED for the T4a lever on purpose. The new MMVQ-style
decode arm behind VT_GEMV_MMVQ=1 will reproduce the CPU oracle's float
association (positional sums[] chains plus the sequential dmin chain of
VecDot{Q4,Q5,Q6}_KQ8_K), so its parity case asserts BIT-exact equality with
host vt::MatmulBTQuant -- strictly tighter than the 1e-6 NMSE band the
baseline warp-reduction kernel can claim, because its __shfl_down tree
reassociates the float sum. Until that dispatch arm exists the flag is
inert, the baseline runs, and the bit-exact compare fails.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…arm -- bit-exact vs the CPU oracle

The T4a capture prices QuantizeQ8KK (2.593 ms/tok, 43.7 launches/tok at
59.3us avg on grids of <=1 block) plus KQuantGemmK (2.943 ms/tok combined)
as the top remaining GPU family. This adds an opt-in decode arm for
kMatmulBTQuant's K-quant branch behind VT_GEMV_MMVQ=1, m==1 only; the flag
is read per call (the cuda_quant_dot.cu convention) and the default path is
byte-unchanged.

Geometry-only change: the warp still owns one output element j, but its 32
lanes walk 32-elem CHUNK units (4 super-blocks x 8 chunks per pass) instead
of lane-strided whole super-blocks, so every lane stays busy down to nsb=1
(the baseline idles 22 of 32 lanes at this model's nsb=10), the scale/min
unpack is split per chunk instead of duplicated per lane, and Q6_K drops
its aux8[256] local-array rebuild for positional in-register dequant.

Numerics are bit-exact vs the host oracle BY CONSTRUCTION, not within a
band: the integer cores are exact under any association, and the float side
reproduces VecDot{Q4,Q5,Q6}_KQ8_K's association exactly (8 positional
per-super-block sums chains accumulated sequentially in super-block order,
plus the sequential dmin*sumi chain). The new focused gate
tests/vt/test_rocm_quant_dot.cpp asserts BIT equality with vt::MatmulBTQuant
on host tensors: 54/54 parity asserts across {Q4_K,Q5_K,Q6_K} x
nsb{1,3,10} x N{1,7,129} x 2 seeds, plus the default-arm 1e-6 NMSE probe.
RED-first recorded: pre-arm build failed 47/55 (the baseline shfl tree
cannot meet bit-exactness); post-fix 55/55, exit 0.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…ologue and widens the gate to engine dtypes

Operator steer from the fresh HEAD capture: QuantizeQ8KK bills ~59us per
launch on grids of <=1 block (2.593 ms/tok across 43.7 launches/tok) -- the
prize is deleting the standalone launch, not shaving the GEMM alone. The
per-super-block body is factored into QuantQ8KSBlock; the new
KQuantGemvMmvqFusedK quantizes the row into block LDS via that SHARED body
(same thread-per-super-block walk as the standalone grid), barriers, then
runs the unchanged GEMV row body against the LDS copy. Byte-identity is by
construction and asserted directly: MmvqQuantScratchForTesting exposes both
semantics and the focused gate memcmps them on pseudo-random rows plus a
tied-amax adversarial row (+max first, equal-magnitude negatives later) and
an all-zero row. The fold engages only when nsb*sizeof(BlockQ8_K) fits 32KiB
LDS; larger rows take the standalone-quant arm.

The parity case now covers the ENGINE'S actual dtypes -- bf16 and f16
activations, bf16 outputs -- after the first acceptance window showed the
op-green/f32-only gate while the engine degenerated: the second instance of
the garbage-fast lesson (T3a was LDS underallocation, T4a is dtype
coverage). Suite: 331 assertions green including ON-vs-OFF byte identity at
model-like shapes (bf16 act/out, K up to 10240, N up to 10240).

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…ive, garbage-fast instance #2

The VT_GEMV_MMVQ=1 arm is bit-exact vs the CPU oracle at the op seam under
every constructed condition (331 assertions: bf16/f16 activations, bf16
outputs, tied-amax quant byte-equality, ON-vs-OFF sweep identity at model
shapes) yet in-engine it degrades the acceptance stream to fluent
number-loops at 5-11 tok/s vs the OFF arm's coherent 34.81 tok/s median.
Interleaved same-window medians recorded with uptimes; graph replay ruled
out; one ON-arm rep wrapped in rocprofv3 for operator attribution (raw db at
/work/t4a-prof-on). Verdict: CLOSED NEGATIVE, flag stays default-OFF, defect
correctable, next hypotheses named (per-layer dispatch trace, geometry-vs-
fold split arms, near-tie adjudication).

Also recorded: mutation log incl. one NOT-caught mutation with the gate gap
named; the gpu-ctl exclusion incident timestamps verbatim; and the
campaign-level lesson that garbage-fast now has two independent instances
(T3a LDS, T4a dtype coverage) -- op parity never substitutes for engine
token-coherence.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…patch and makes the GEMV bit-equal to the baseline kernel

Two defects closed red-first against the round-1 arm, which was op-green
but engine-garbage and uniformly slow.

Defect 1 (the engine killer): round-1 gated only the LDS fold on m==1, so
the non-fused GEMV branch captured EVERY multi-row call -- including the
engine's observed m=39 prefill chunks -- and the GEMV kernels write row 0
only, leaving rows 1..m-1 of prefill outputs UNWRITTEN while every op-level
test (all m==1) stayed green. The m==1 restriction now gates gemv_mmvq
itself. Red-first: a new MULTI-M gate case (m in {3,39} incl. the real
18432x2560 prefill shape, canary-filled outputs) fails 4/4 at the unfixed
code and passes after the one-line gate move.

The GEMV row body is also now BIT-EQUAL TO THE BASELINE KQuantGemmK output
at every (Fmt, nsb, j), not merely oracle-exact: the octet chunk-walk keeps
the exact integer phase (dp4a word cores; Q6_K nibble bias removed exactly
in the integer domain via a constant-word dp4a), then each super-block's
float term is reconstructed as the baseline's own d*isum (- dmin*sumi)
expression and replayed under the baseline's lane ownership and
__shfl_down(16,8,4,2,1) tree. ON==OFF byte identity at every shape is now
by construction, asserted by an extended sweep over the REAL model shape
set from the checkpoint GGUF manifest plus all exact engine dispatch tuples
-- including lm_head-class N=151936 and N=248320 (max offset 0.52 GB < 2^31,
falsifying the 32-bit-overflow hypothesis; the sweep's round-1 reds were
isolated near-tie rows from the oracle-vs-baseline ULP difference).
QuantQ8KSBlock also loads each activation once instead of twice (same
values). Perf defect: per-grid timing shows the fused fold's per-block
requantization scales with n/4 and loses 2.15-3.3x at lm_head-class grids,
so the fold is now gated to n <= 512 where it measurably wins; the
non-fused arm beats KQuantGemmK at every captured grid (0.31x-0.85x).

Focused suite: 719/719 across 6 cases incl. multi-m, engine-shape sweep,
and timing evidence. Mutations M-A (ownership predicate), M-B (qh 2-bit
mask), M-C (dmin term), M-D (m-gate removal) each caught and restored
byte-equal. Engine A/B: interleaved x5+5 same-window medians OFF 35.751 vs
ON 40.508 tok/s (+13.2%), all five ON outputs byte-identical to their OFF
pairs.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…+13.2% median with byte-identical engine outputs

Amends the T4a evidence file (history retained) with the fourth-session
repair cycle: the operator's round-1 ON-capture parse; the red-first
extended ON-vs-OFF sweep over the real model shape set incl. lm_head-class
N=151936/248320, which falsifies the 32-bit-overflow hypothesis (max offset
0.52 GB < 2^31; isolated near-tie-row ULP signature instead); the TRUE
defect-1 root cause -- the m-gate hole that let m=39 prefill chunks into
the row-0-only GEMV branch, proven red by a canary-filled MULTI-M gate case
and by an instrumented dispatch trace; the baseline-bit-equal GEMV rewrite;
the per-grid timing table isolating the fused fold's block-count-scaled
requant cost and the measured n<=512 hybrid crossover; mutation log
additions M-A..M-D; and the post-repair acceptance A/B -- interleaved x5+5
same-window medians OFF 35.751 vs ON 40.508 tok/s (+13.2%), all five ON
outputs byte-identical to their OFF pairs. Verdict updated to LEVER
ADOPTED; flag stays default-OFF.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…ers and the F1/F2 routing-witness gate cases

Closes the two reviewer findings that the round-1 gate could not see:
(1) no case exercised VT_GEMV_MMVQ truly unset (EnvGuard(false) writes "0")
while ON==OFF are bit-equal by construction, so outputs cannot witness
routing; (2) nothing detected a kMmvqFoldMaxRows crossover drift, and the
reviewer's 512->4096 mutation went fully green while flipping measured
per-call ratios. The fix is the operator-contracted shape: process-global
HOST-side counters bumped once per MatmulBTQuantKernelRocm dispatch on
exactly the branch taken (baseline / non-fused GEMV / fused fold), exposed
via MmvqRouteCountsForTesting + a reset hook; during stream capture kernel
launches are recorded as graph nodes and not executed, so replay
multiplicity can never skew the witness. Two new cases assert true-unset
routes to baseline with the ON pair asserting the reverse, and pin the fold
crossover at n=256 vs n=2304. Red-first proven: both cases fail to link
before the seam exists. Replay of reviewer mutation M3 (getenv default
inverted) is caught by F1 and M4 (fold cap 512->4096) by F2; restores are
byte-equal (md5 5419b3f91dcdbb2321db823c60063f06). Focused suite green at
731/731 assertions; spec ctest gate unchanged vs a proven HEAD baseline;
0.8B engine coherence smoke byte-identical across arms. Evidence section
13 appended.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…hind VT_GEMV_MMVQ_FOLD_MAX

The fresh capture at b80a0bd prices the n>512 shapes' standalone
QuantizeQ8KK launches at 2.177 ms/tok -- the top remaining GPU item --
while reviewer-mutation M4 evidence shows the fused fold leg running 1.30x
baseline at grid=576 vs 0.53x unfolded, so folding wins whenever the
deleted ~50us quant launch outweighs the folded-call penalty. Whether that
net win extends past the shipped 512-row crossover is an empirical,
end-to-end question, so this change makes the crossover runtime-tunable for
measurement: VT_GEMV_MMVQ_FOLD_MAX takes integer rows, defaults to
kMmvqFoldMaxRowsDefault (512, unchanged), and falls back to the default on
empty or invalid values; it is read per call like VT_GEMV_MMVQ so in-process
tests and graph capture pick it up at dispatch time. Default behavior is
byte-unchanged and stays pinned by the F2 constants; the new F3 witness
case asserts through the existing host-side route counters that the env
actually moves routing both ways (n=2304 folds at 4096, n=256 stops folding
at 128), that the boundary is inclusive, and that garbage values fall back
to the default. Red-first proven: before the knob exists the widening and
narrowing legs fail while every default-pinning leg passes (exit 1).
Mutations M-B1 (knob inert) and M-B2 (<= narrowed to <) are both caught by
F3; restores are byte-equal (md5 e0841e2083c1d85e75617c0b2f248df2). Full
focused suite green at 752/752 assertions across 9 cases.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…e closed negative, 512 already optimal

Section 14 records the full B1 session: the VT_GEMV_MMVQ_FOLD_MAX knob and
F3 routing witness (red-first, mutations M-B1/M-B2 caught, suite green at
752/752), the interleaved three-arm acceptance-workload A/B under gpu-ctl
(OFF 35.594 / ON-default 40.348 / ON-FOLD_MAX=4096 36.142 median tok/s --
on4096 loses in all five paired triads), the middle-value refinement probe
(1024 ties default at 40.149 vs 40.305), byte-identical coherence across
all arms (md5 2b29ad66eea3ee3a99ff0694127ce88f, same as the adopted
window), four honestly-recorded protocol incidents, and the negative-close
verdict with the knob kept inert-documented: the fold's per-block requant
already outweighs the deleted ~50us quant launch at the first n>512 shape
class, so the 2.177 ms/tok standalone-quant item needs a cooperative-quant
or graph-level fusion instead.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…k to the two f32-out GDN BA projections

Per-site attribution of the rank-2 GPU item (Cijk_Alik_Bljk_BSS_BH_
MT128x32x16_SE_1LDSB0, 21.6 calls/tok amortized at ~73.6us) from the
fresh capture DB, committed before any kernel code per the lever-B2
contract. Parsing the rocprofv3 dispatch stream and correlating it with
the GdnBlock op order and the GGUF tensor map shows the 12288-call
population closes exactly as 48 calls per decode step x 255 steps plus
one prefill pass: the Qwen3.5 GDN blocks' in_proj_b/in_proj_a
(ssm_beta/ssm_alpha, N=32 x K=2560). Both sites emit f32 (ProjectGdnBA,
qwen3_5.cpp:3663-3664), so every decode-skinny gate in
MatmulBTKernelRocm -- which requires a bf16 output -- skips them and
they land on hipblasGemmEx -> rocBLAS's large-M Tensile tile: ~73.7us
to stream a 164 KiB weight, ~3.54 ms/tok combined under graph replay,
100% of the arm-coverage target. The sibling bf16 projections (in_proj_
qkv/z, out_proj) already ride wvSplitKSml at bandwidth-bound times.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…ivity quantified

The rmsnorm-row LDS-epilogue attempt measured -38% (LDS bank conflicts on
u16 lane-strided reads vs an L1-resident gmem re-read) and was reverted
byte-restored. Separately, byte-identical code measured 92.9 tok/s at
load ~3.5 but 53-58 under sibling host contention at load ~5.5: launch-
bound decode tracks host scheduling. Rule added — acceptance numbers are
only comparable at recorded host load; untraced absolute claims need
load < ~4, paired A/Bs stay valid under matched conditions.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…he rate picture

Assigning all 72 per-step split-K calls to step positions across 505 steady
steps resolves three clean sites at 46.0/23.7/26.5us — attn_qkv at 456
GB/s, attn_gate 442, ssm_out 396 — replacing the blended 700 GB/s figure
and restoring real headroom (~0.5 ms/tok to a 550-600 GB/s practical
target). The concrete lever is a per-shape launch-config sweep of the
donor's kYtile/wvPrGrp/split-factor math for exactly these three (N,K)
shapes on gfx1100.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
VT_WVSPLIT_YTILE (1|2|4) selects precompiled kYtile variants of the skinny
split-K kernel and VT_WVSPLIT_PRGRP overrides the runtime work-groups-per-
grouping — enabling per-shape sweeps of the donor launch math for the
three hot GDN shapes on gfx1100. Sweep under host load ~5 found YTILE=4
directionally positive (+1.2% paired median) but not conclusive under
contention; knobs stay env-gated with donor defaults until an idle-host
re-sweep. The f32-out lever-B2 arm keeps donor geometry regardless.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
The 10:41Z watcher-run chain included the T14 arms: OFF median 82.180 vs
ON 82.897 tok/s across five interleaved pairs, all byte-identical as the
bit-deterministic argmax requires. The lever's pending tok/s A/B is
closed; session total stands at ~92.8 tok/s canonical with every adopted
lever enabled.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…ention

taskset to the free cores under load ~5.5 reads identically to unpinned:
the sibling services' pressure is host memory bandwidth, which launch-
bound decode cannot be shielded from by core selection. Confirms idle-
host as the only valid absolute-measurement condition.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…anchors

The sync-loop deferral is smaller than assumed: EngineCore::step already
supports depth-2 pipelining via sample_tokens_async, and the only blocker
was QueueSupportsAsyncInputCombine — which the real event primitives make
TRUE on ROCm. The plan enables VT_ASYNC_RUNNER=1 in the acceptance config,
verifies the depth-2 drain engages, and A/Bs paired x5 through the CLI.
Fallback is the async-serving path with its ~40% overhead attributed first.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…tical

The idle-sweep watcher fired and ran the conclusive YT4 vs baseline
paired verification: ON wins all five pairs (+1.8% median, 52.95->53.91).
Output is bit-identical on a separate coherence check. The pre-committed
decision rule (adopt iff ON wins >=4/5) is satisfied. Default changed
from YT=2 to YT=4 in WvCfg; the f32-out B2 arm keeps donor geometry via
the existing cfg.yt!=2 guard. Gate 16/16, 839 assertions.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…torm closed, roofline analysis

Acceptance gate at load 1.45: median 100.46 tok/s (runs 2-6, +8.1% from
YT4 adoption under unconstrained bandwidth). T13 async-runner A/B is a
wash on CLI (depth-1 drain, all byte-identical) — closed for CLI path.
Copy-storm attribution: per-step small copies total 0.035 ms/tok,
negligible — closed as a lever. Roofline: model 2.74 GB at 800 GB/s =
3.43 ms minimum weight read; wvSplitKSml at 57% bandwidth is the top
headroom target. Next: v_dot2_f32_bf16 to reduce compute bottleneck.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…y-bound kernel

The dot2 instruction replaces 5 scalar f32 ops with 1 per bf16x2 pair,
but wvSplitKSml is memory-bound at 57% bandwidth — compute is already
hidden behind memory latency. Paired A/B on idle host (load 0.55): OFF
88.784 vs ON 88.897 (+0.13%, WASH). All 5 pairs differ (reduction order
change) but both outputs are coherent analytic prose. The runtime branch
also caused a 12% regression in the OFF path from code-size pressure;
reverted and 100.47 tok/s confirmed restored.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:ox-alpha [omp]
…vqRow

Replace scalar Dp4a emulation with amd_mixed_dot(char4,char4,...) in the
MMVQ Q4_K/Q5_K/Q6_K inner loops. The scalar Dp4a pattern
(reinterpret_cast<int8_t*> + multiply + add) does NOT auto-lower to
v_dot4_i32_iu8 when the input comes from a shift+mask expression because
the compiler cannot prove the values fit in 8 bits. amd_mixed_dot forces
the hardware dot instruction, reducing instruction count by ~10x and
VGPRs by 30-43%.

ISA verification (gfx1100, -O3):
  Q4_K:  0->8 v_dot4, 31->0 v_mul, 62->6 v_bfe, 68->42 VGPRs
  Q5_K:  0->8 v_dot4, 31->0 v_mul, 46->6 v_bfe, 73->51 VGPRs
  Q6_K:  0->16 v_dot4, 30->0 v_mul, 46->6 v_bfe, 86->49 VGPRs

Correctness: gate 16/16, 839 assertions. Output BYTE-IDENTICAL to
scalar baseline (integer arithmetic is exact regardless of evaluation
order). Body coherence verified — analytic prose, no degeneration.

Matched-load A/B (loadavg ~1.0, 5 runs each):
  baseline median: 90.133 tok/s
  v_dot4  median: 92.570 tok/s  (+2.7%, all fixed > all baseline)

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:glm-5-2 [omp]
…ted — engine wash

T20 rewrote KQuantGemvMmvqRow for full-warp cooperation (32 threads per
super-block, zero intermediate barriers, single warp_reduce_sum). The
kernel wins 2.4-3.1x on large grids (lm_head Q6_K 248320x2560: 2134->682us)
but the engine A/B is a wash: ON 92.9 vs OFF 92.8 tok/s median (5 paired
reps, full campaign config).

The kernel win doesn't reach the engine because the dominant Q4_K path
(2.46 ms/tok, 25% of wall) has small grids (ffn_gate/up ~576 rows) where
the kernel is launch-overhead-bound, not reduction-barrier-bound. The Q6_K
path (1.20 ms/tok) is mostly small-grid ffn_down (22 calls/tok, 1.03x).
The large-grid lm_head save is 1 call/tok — invisible when averaged.

Conclusion: kernel micro-optimization is exhausted for the dominant paths.
The 4.2 ms/tok overhead above the 4.38 ms/tok weight-read floor is the
bottleneck. Path to 200 tok/s requires launch-overhead reduction (HIP
graph capture, kernel fusion, or persistent kernels).

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]
…ctions

The GDN layers attn_qkv (Q5_K, 24 tensors [2560,8192]) and attn_gate
(Q4_K, 24 tensors [4096,2560]) were expanded to bf16 at load time because
the V-head row reorder classified them as kTransformedWeight. The reorder
is a ROW permutation — quantization blocks are along the K (column)
dimension and are self-contained per row — so it is block-safe. T21 routes
these tensors as kMatmulWeight to allow keep-quant, copies the blocks via
OwnGgufQuantBlocks(mmap_src=nullptr), and applies ReorderVRows to the
block bytes at load time. The forward pass already dispatches quantized
nk=true weights through vt::MatmulBT, so no forward-pass change was needed.

A/B: +3.9% (87.4 to 90.8 tok/s median, 5/5 pairs). Gate 16/16, 839
assertions. Output coherent but not byte-identical (Q5_K integer dot
product vs bf16 float MAC). VT_GDN_ROWPERM_KEEP_QUANT=0 reverts to the
old bf16 expansion path for A/B isolation.

The improvement is less than the projected 14% because the Q5_K GEMV
kernel has lower effective bandwidth on small grids (n=2560) than
assumed, and wvSplitKSml is more efficient on these grids than projected.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:glm-5-2 [omp]
…consumers

The prior bridge invalidated the producer token on any non-matching
K-quant consumer query, forcing standalone QuantizeQ8KK launches when
a non-matching GEMV (e.g. attn o_proj reading the attention output)
queried between the norm producer and a matching consumer that shares
the normed hidden state. A non-matching consumer reads a DIFFERENT
activation buffer and cannot stale the token — only a new producer
overwrites it. Removing the invalidation lets matching consumers
reuse the fused scratch even after irrelevant queries intervene.

The gate test confirms: a non-matching query no longer invalidates,
and a subsequent shape-matching call on the original buffer still
reuses the fused scratch (consumers_fused == 1, was 0). Byte-identical
outputs (5/5 A/B pairs, 1068 bytes). Gate 16/16, 839 assertions.

Correctness: the token records the PRODUCER's output pointer and
scratch. A non-matching consumer reads a different buffer that cannot
overwrite the producer's output or scratch. The token is overwritten
only when a new producer calls NormQuantRecordProducer. Stream-ordering
is unchanged (single stream, sequential dispatch).

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [TOOL]
…pKernel

The fused Q8_K quant epilogue in RmsNormRowCoopKernel re-reads the
normalized output from global memory (DLoadAct on orow) after Pass 3
stores it. On gfx1100 the 5 KB bf16 row (h=2560) competes with the
weight and input in the 16 KB L1, so the re-read can miss to L2.

T24 stores the normalized row to dynamic shared memory during Pass 3
(when the value is already in registers) and reads from LDS in the
quant epilogue, eliminating the global re-read. The LDS buffer is
h * sizeof(Tout) bytes (5 KB for bf16 h=2560), well within the 64 KB
per-CU limit.

Env gate VT_RMSNORM_LDS_QUANT (default ON) controls the optimization:
set to 0 to revert to the global re-read path for A/B isolation. The
gate is read per-call so captured graphs and in-process tests pick it
up at dispatch time.

Byte-identity: the LDS store uses the same conversion as Store (bf16
RNE for bf16 output, exact copy for f32), and DLoadAct reads the same
bytes from LDS as from global. Gate test: 16/16 cases, 839 assertions,
all passed.

A/B measurement pending: the co-tenant 27B model holds the GPU VRAM,
blocking the acceptance workload. The A/B script is staged at
agent-artifacts/tg200-t24/ab-t24.sh for when the GPU is available.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]
ssm_out (out_proj) is Q5_K in the GGUF checkpoint but was expanded to bf16
at load time because the V-head column reorder (ReorderVCols) cuts across
Q5_K 256-element block boundaries. T25 keeps the weight in tiled Q5_K order
(no ReorderVCols) and permutes the 4096-element GEMV input from grouped to
tiled order at runtime instead, cutting weight bandwidth ~4x (Q5_K ~5 MB vs
bf16 20 MB per call).

The permutation is a simple gather of 128-element groups within each of the
4096-element rows, gated by VT_GDN_COLPERM_KEEP_QUANT=1 (default OFF). A new
out_proj_tiled flag on GdnLayerWeights distinguishes the tiled Q5_K path
(needs input permutation) from the gdn_expand_nk bf16 path (already
column-reordered, no permutation needed) — the nk flag alone conflates both.

A/B (5 interleaved pairs, --max-tokens 256 --temperature 0 --seed 0):
OFF median=90.930 tok/s, ON median=91.703 tok/s, +0.85%, 5/5 ON>OFF.
Output coherent but NOT byte-identical (Q5_K vs bf16 weight precision).
Gate test: 16/16, 839 assertions.

The improvement is modest because the permutation kernel launch overhead
(~13.4 us x 24 calls = ~322 us/tok) offsets most of the weight bandwidth
savings (~368 us/tok). The net gain is ~46 us/tok.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]
The standalone QuantizeQ8KK kernel used 1 thread per 256-element superblock,
each doing a serial scan of 256 elements (~800 instructions). For decode
(m=1, nsb=10) only 10 of 128 threads were active, and on wave32 each thread
is its own wave, so the kernel took ~13.4 us/call = 540 us/tok (6.0% of
wall time).

The new QuantizeQ8KKWarpCoop kernel uses 8 threads per superblock (32
elements each). The amax scan is done per-chunk (ascending, ax > amax
first-occurrence), then reduced across 8 threads via __shfl_xor_sync with
lower-chunk-index tie-break — equivalent to a sequential scan of all 256
elements. The quantization (iscale = -127/mx, DNearestInt, clamp 127) and
bsums are order-independent. Output is BYTE-IDENTICAL to the original
QuantQ8KSBlock, asserted by the gate test (16/16, 839 assertions) under
VT_QUANT_Q8K_WARP=1.

For m=1, nsb=10: 1 block, 80/128 threads active (vs 10/128), 3 waves of
~100 instructions (vs 10 waves of ~800) = ~8x fewer wave-cycles.

A/B on acceptance workload (Qwen3.5-4B Q4_K_M, 256 tokens, temp 0, seed 0):
  OFF median: 91.532 tok/s
  ON  median: 93.417 tok/s
  +2.06%, 5/5 pairs ON>OFF, all 5 byte-identical (1039 bytes)

Gated by VT_QUANT_Q8K_WARP (default OFF, read per-call).

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]
The Q6_K GEMV inner loop ran two amd_mixed_dot calls per iteration:
one for the value dot product (sub = v . W8) and one for the bias
correction (pre = 32 . W8), then subtracted. Since (v - 0x20) . W8
equals sub - pre exactly, subtracting the bias word before the dot
product eliminates the second dp4a entirely. Unsigned byte subtraction
wraps to the correct signed char value (v < 32 maps to 256+v-32, which
is signed v-32), so the char4 reinterpret is correct.

Also sets kGemvWarps from 4 to 8 for better L2 weight sharing on
gfx1100; measured neutral-to-positive across the acceptance workload.

Measured: 96.0 tok/s median (5 reps, 256 tokens, all coop + GQA4 env
vars), unchanged from baseline. The Q6_K path is 15% of decode GPU time
and the kernel is memory-bound, so halving the dot-product count does
not move the wall clock. The change is kept because it reduces ALU
pressure without a correctness cost.

Correctness: test_rocm_quant_dot passes. The fused dot product is
bit-exact with the original two-dot formulation because unsigned
subtraction modulo 256 equals signed subtraction for 8-bit values.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:glm-5-2 [omp]
S1-S3 complete at 96.0 tok/s median (target 150). The campaign
exhausted optimization attempts within the allowed edit scope
(src/vt/rocm/ and tests/vt/). The Outcome section records what was
measured, what was rejected, why each default has its value, and the
next traceable hypothesis: porting the CUDA-only async device mirror
and executor to ROCm (requires runner.cpp changes outside the allowed
scope) and a fundamentally different GEMV kernel design for >53%
bandwidth efficiency.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:glm-5-2 [omp]
Brings in the GFX1100-TG150 spec (.agents/specs/gfx1100-tg150.md) and its
issue-index row. Pure spec addition, no code changes.
Brings in the KERNEL-QUANT-CIQ-GEMM-ROCM W1 keep-quant providers
(rocm_quant_dot.hip) and the ROCM-QUANT-GEMM-BW bandwidth work (split-K
decode, branch-free Q6K, f32-query GQA, row-permuted keep-quant).

Both quant-dot paths coexist: TG200's rocm_grouped_gemm.hip routing and
CIQ/BW's dedicated rocm_quant_dot.hip provider.

# Conflicts:
#	CMakeLists.txt
#	src/vllm/model_executor/models/qwen3_5_gguf_weights.cpp
#	src/vt/rocm/rocm_paged_attn.hip
#	tests/CMakeLists.txt
#	tests/vt/test_rocm_quant_dot.cpp
Cherry-pick 872b53e onto row/GFX1100-TG200 to enable fp8 KV cache
on the production server. Resolved tests/CMakeLists.txt conflict by
keeping both the TG200 test additions and the new test_rocm_fp8_kv_cache.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]
)

The fp8 KV cache store and correctness-grade read landed in W6
(fp8-kv-cache.md ## W6), but the fp8 read through the fast decode
kernel is owed: --kv-cache-dtype fp8 forces PagedAttnOnline because
every optimized decode kernel requires k_cache.dtype == kBF16. Measured
7.5x regression at 16K context vs bf16 KV on gfx1100.

This spec covers widening the VT_ATTN_DECODE_GQA4 dispatch guard to
accept kI8 KV and adding an fp8 dequant load path inside the GQA kernel.
The dequant arithmetic already exists in LoadKv(uint8_t*, ...) at
rocm_paged_attn.hip:176; the fast kernel just does not call it.

Fork issue #7. Separate spec and implementation PRs (developer
preference 2026-08-27).

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]
Widen the VT_ATTN_DECODE_GQA4 dispatch guard to accept DType::kI8 KV
cache when kv_cache_dtype != kAuto, and add an fp8 dequant load path
inside the GQA kernel.

Previously --kv-cache-dtype fp8 forced PagedAttnOnline (the reference
kernel with per-key __syncthreads reduction) because every optimized
decode kernel required k_cache.dtype == kBF16. Measured 7.5x regression
at 16K context vs bf16 KV on gfx1100.

Changes to rocm_paged_attn.hip:
- Template PagedAttnDecodeGqaF32Q on TKV (default __hip_bfloat16) with
  k_scale/v_scale parameters
- Add LoadRowEplFp8<EPL>: vectorized uint8_t loads (uint32_t for EPL=4,
  uint2 for EPL=8, uint4 for EPL=16) with F8E4M3ToF32Dev dequant + scale
- Add LoadRowEplKv<EPL, TKV>: if constexpr dispatch to LoadRowEplBf16 or
  LoadRowEplFp8
- Widen dispatch guard: kBF16||kI8 with kv_cache_dtype != kAuto
- Add fp8 launch paths: PagedAttnDecodeGqaF32Q<...,uint8_t> with
  args.k_scale/args.v_scale

The dequant arithmetic (F8E4M3ToF32Dev * scale) is identical to the
existing LoadKv(uint8_t*, ..., scale) at line 176, so the fp8 read
values are bit-identical between the fast and fallback kernels.

A/B benchmark (Qwen3.5-4B Q4_K_M, RX 7900 XTX, ROCm 7.14.0, 128-token
greedy decode, 4 reps):
  ctx=256:   99.94 -> 140.19 tok/s (1.40x), bf16=143.15 (0.98x)
  ctx=1024:  56.28 -> 122.31 tok/s (2.17x), bf16=129.02 (0.95x)
  ctx=4096:  20.53 ->  79.53 tok/s (3.87x), bf16=92.08 (0.86x)
  ctx=8192:  11.08 ->  54.55 tok/s (4.93x), bf16=66.85 (0.82x)
  ctx=16384:  5.78 ->  33.47 tok/s (5.79x), bf16=43.16 (0.78x)

Token-exact: 3/3 simple prompts match the PagedAttnOnline fallback
output byte-for-byte. Divergence at thinking-tag tokens is the
reduction-order risk the bf16 GQA4 path already carries.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]
Replaces the serial <<<n,1>>> random-sample kernel with a block-cooperative
argmax reduction over Gumbel scores, mirroring the CUDA fix from mudler#1984.
The RNG and argmax reduce come from include/vt/sample_common.h, shared
across CPU/CUDA/ROCm so bit-identity is a build property, not a copy-sync
property. The serial path is retained behind VT_FAST_RANDOM_SAMPLE=0 for
same-binary A/B gating.

Adds ROCm + CUDA subprocess A/B test cases that re-exec the binary with
VT_FAST_RANDOM_SAMPLE=0 and =1 and assert byte-identical token ids across
widths up to 248320 (Qwen3.8-27B vocab).

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:MODEL [TOOL]
…mplate fallback

backend.h: RocmAttentionBackend now advertises fp8 and fp8_e4m3 in
supported_kv_cache_dtypes, matching the fp8-e4m3 KV cache read path
in rocm_paged_attn.hip (W6).

server_main.cpp: a .gguf model has no tokenizer_config.json — its chat
template lives in GGUF metadata. Falls back to LoadChatTemplateFromGguf
when the config path throws and the model is a .gguf file.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:MODEL [TOOL]
upstream/main moved 101 commits past this branch's base (a73b269):
the SAMPLE-CORE parallel Gumbel draw (mudler#1984/mudler#1997), SAMPLE-PROMPT-LOGPROBS,
the SPEC-DFLASH2 waves, and the LTX-2 oracle tooling. Two files conflicted:

- scripts/env-doc-allowlist.txt: this branch sorted the list and dropped
  the orphaned comment block; upstream appended VT_TT_SLOT_TRACE,
  VT_DUMP_QKVZ, VT_DUMP_TRUST around VT_FAST_RANDOM_SAMPLE. Kept the
  sorted shape and folded the four upstream names in.
- tests/vt/test_ops_sample.cpp: both sides carry the mudler#1997 sampling
  work (this branch cherry-picked it as 0617b3f), so the auto-merge
  reunited the shared primitives; kept this branch's ROCm random_sample
  suites verbatim where upstream added nothing beside them.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5.3 [OMP]
…d red

check-agent-record and check-env-doc both failed on the branch head, so
no push of this row could chain a green gate.

- issue-index: mudler#1586 was listed twice (BACKEND-ROCM umbrella row and the
  ROCM-QUANT-GEMM-BW row). The umbrella row keeps the link; the quant-gemm
  row's spec already records "Owned under issue mudler#1586", and the duplicate
  is what the checker refuses. Dropped the duplicate append before it
  lands, so no union merge can resurrect it.
- issue-index: the #7 row carried `kBF16 || kI8` verbatim, and the two
  pipes split the table row (7 pipes, 5 expected). Reworded to "accept
  `kI8` KV as well".
- engine-matrix: the upstream-main merge shifted qwen3_5_gguf_weights.cpp
  by +52/+131 lines; re-anchored LoadGgufSharedEmbedAndHeadBf16 to :1067
  and LoadQwen3_5MTPFromGguf to :1556, restoring anchor rot to the
  upstream baseline (33).
- env-doc-allowlist: the keep-quant campaign kernels read eight
  kernel-internal knobs (VT_ASYNC_DEBUG, VT_GDN_COLPERM_KEEP_QUANT,
  VT_GDN_ROWPERM_KEEP_QUANT, VT_MM_TRACE, VT_QDOT_SPLITK, VT_QDOT_TRACE,
  VT_QUANT_Q8K_WARP, VT_RMSNORM_LDS_QUANT) that never made the list;
  allowlisted in sorted position.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5.3 [OMP]
Conflict resolutions taken from row/GFX1100-ALL, which already combined
this campaign with later main: LoadChatTemplateForModel in server_main
(mudler#2077/mudler#2079), keep-quant grouped GEMM plus mudler#1910 Q6_K coop, and both
campaign and upstream test registrations.

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:cursor-grok-4.6 [OMP]
…code-attn

Keep the campaign performance work (including bf16 PagedAttnDecodeGqaF32Q)
and upstream W6 store/read. The fast fp8 GQA path, dtype advertise, and
rocm-fp8-kv-decode-attn spec move to row/fp8-kv-decode-attn.

FOLLOWING_AGENTS_PROTOCOL: true
AI-Assisted: true
Assisted-by: AGENT:glm-5-2 [OMP]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant