perf(GFX1100-TG200): T8 cooperative single-row rmsnorm arm - #2874
Open
ghazni101 wants to merge 8 commits into
Open
perf(GFX1100-TG200): T8 cooperative single-row rmsnorm arm#2874ghazni101 wants to merge 8 commits into
ghazni101 wants to merge 8 commits into
Conversation
… 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]
Adds the VT_GEMV_MMVQ=1 opt-in K-quant decode GEMV arm for MatmulBTQuant, bit-exact vs the CPU oracle. The arm folds activation quant into the MMVQ GEMV prologue (deleting the standalone QuantizeQ8KK launch) and widens the gate to engine dtypes (bf16/f16 activations, bf16/f32 outputs). Sub-levers: - lever-B1: VT_GEMV_MMVQ_FOLD_MAX makes the fold crossover tunable at runtime - lever-B2: VT_SKINNY_BF16=1 f32-out decode-skinny arm for GDN BA projections - repair: m-gates the whole dispatch and makes the GEMV bit-equal to baseline - repair-2: host-side dispatch-route counters + F1/F2 routing-witness gates - lever-B2 test: red-first f32-out decode-skinny gate, true-unset routing window Architecture: F1 moved the live MatmulBTQuantKernelRocm to rocm_quant_dot.hip (anonymous namespace, internal linkage). T4a's MMVQ arm lives in rocm_grouped_gemm.hip's version (external linkage, renamed to *Gdn). This PR adds delegation: rocm_quant_dot.hip forwards Q4_K/Q5_K/Q6_K calls to the Gdn version, preserving F1's IQ-type providers while activating T4a's MMVQ arm. The default path (VT_GEMV_MMVQ unset) is byte-unchanged from F1. The arm is opt-in and validated by test_rocm_quant_dot (6/6 cases, 719 assertions) and test_rocm_skinny_f32 (2/2 cases, 51 assertions). Token-identical to upstream baseline on Qwen3.5-4B Q4_K, 32-token greedy decode, seed 0. Depends on mudler#2782 (F1 keep-quant GEMM infra). FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:GLM-5-2 [OMP]
…Norm epilogue Lever-C adds an opt-in fused norm-quant epilogue (VT_NORM_QUANT_FUSED=1): RmsNormRowKernel emits the row's Q8_K blocks alongside its normal output, and MatmulBTQuant's K-quant branch skips the standalone QuantizeQ8KK when the consuming activation matches the producer token. Byte-identical to the standalone path by construction (shared QuantQ8KSBlock body). New files: - src/vt/rocm/rocm_act_quant.h: shared Q8_K quant-block body - src/vt/rocm/rocm_norm_quant_bridge.h: producer-consumer token contract Also fixes T4a routing counter placement (moved outside anonymous namespace for external linkage) and restores VT_GEMV_MMVQ_FOLD_MAX env var reading that was lost during cherry-pick conflict resolution. The default path (VT_NORM_QUANT_FUSED unset) is byte-unchanged. Validated by test_rocm_quant_dot (12/12 cases, 797 assertions). Token-identical to upstream baseline on Qwen3.5-4B Q4_K, 32-token greedy decode, seed 0. Depends on mudler#2782 (F1) and mudler#2790 (T4a). FOLLOWING_AGENTS_PROTOCOL 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] FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:GLM [omp]
…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] FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:GLM [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] FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:GLM [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] FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:GLM [omp]
Decode launches one 256-thread block per row and the ported body chains three strided scalar passes, a nine-step __syncthreads tree, and lever-C's per-superblock serial quant walk — 65 launches/tok x 18.1us = 1.178 ms/tok of almost pure latency. VT_RMSNORM_ROW_COOP=1 (default OFF) selects a remap with wavefront shfl reductions (two barriers, width from warpSize), 16-byte vector passes with scalar fallback, and a block-cooperative q8 epilogue whose left-biased max reproduces the scalar scan's first-occurrence (mx, amax) bitwise, so the fused-scratch byte contract holds by construction. The reduction association changes: outputs may move within float ULPs, the flag rides the campaign config opt-in like its siblings, and the teacher-forced ceremony stays owed before any default flip. Focused gate: full suite 13/13 cases, 821 assertions including the new tied-amax scratch-identity case. FOLLOWING_AGENTS_PROTOCOL Following-Agents-Protocol: true AI-Assisted: true Assisted-by: AGENT:ox-alpha [omp]
ghazni101
force-pushed
the
row/GFX1100-TG200-T8
branch
from
September 4, 2026 07:46
8ea44eb to
960a94f
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2871.
Row:
GFX1100-TG200T8 adds a cooperative single-row rmsnorm arm in
rocm_rmsnorm.hip, opt-inbehind
VT_RMSNORM_ROW_COOP=1(default OFF). The cooperative kerneldistributes the rmsnorm reduction across warps within a single row.
A/B benchmark (interleaved, 5 pairs, Qwen3.5-4B Q4_K_M, 256 tokens)
+14.5% over T25 parent. The largest single-lever improvement in the
campaign. Brings the stack to +6.8% above baseline (51.9 vs 48.5 tok/s).
Token-identical.
Depends on the full T25 chain: #2782, #2790, #2792, #2796, #2800, #2804, #2807.
FOLLOWING_AGENTS_PROTOCOL
Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: AGENT:GLM-5-2 [OMP]