Skip to content

feat(KERNEL-QUANT-CIQ-GEMM-ROCM): keep-quant GEMM providers on kROCM - #1

Closed
ghazni101 wants to merge 3 commits into
mainfrom
row/KERNEL-QUANT-CIQ-GEMM-ROCM
Closed

feat(KERNEL-QUANT-CIQ-GEMM-ROCM): keep-quant GEMM providers on kROCM#1
ghazni101 wants to merge 3 commits into
mainfrom
row/KERNEL-QUANT-CIQ-GEMM-ROCM

Conversation

@ghazni101

Copy link
Copy Markdown
Owner

What changed

W1 of KERNEL-QUANT-CIQ-GEMM-ROCM (mudler#1587): the kROCM providers for
OpId::kMatmulBTQuant and OpId::kMatmulBTQuantGrouped, in the new
src/vt/rocm/rocm_quant_dot.hip, plus the committed spec
(.agents/specs/kernel-quant-ciq-gemm-rocm.md) and a red-first gate
(tests/vt/test_rocm_quant_dot.cpp).

Registering the providers flips GgufQuantComputeAvailable() true on
ROCm, so every GGUF k-quant model now computes on the AMD GPU through
the existing seams (loader policy -> vt::MatmulBT[Quant] ->
GetOp(kROCM)) with zero model-path edits. Towers stage once through
the existing ResidentWeight path.

Coverage mirrors the CUDA sibling exactly: ten Q8_K-family encodings
(Q2_K, Q3_K, Q4_K, Q5_K, Q6_K, IQ2_XXS, IQ3_XXS, IQ2_S, IQ1_S,
IQ1_XXXS) plus a native Q8_0 arm. The IQ codebooks reuse
cuda_quant_iq_tables.cuh as the single source — pure __device__
syntax, no CUDA — so no transcription drift is possible.

Why

The ROCm backend had no quantized-weight GEMM provider: every GGUF
k-quant weight on an AMD card computed off device (bf16 expansion at
load). AGENTS.md makes quantized arms a standing requirement. Upstream
pins an RDNA3 W4A16 family in csrc/rocm (gptq_gemm_rdna3 gated
VLLM_ROCM_GFX1100) but nothing in this tree can reach it yet — no
AWQ/GPTQ consumer exists — so that family stays under ## Owed in the
spec (W2), per the reachability rule.

HIP delta that shaped the implementation (measured on gfx1100 /

ROCm 7.14.0)

gfx1100 exposes NO signed byte dot: __dp4a does not exist;
__builtin_amdgcn_sdot4 needs target feature dot1-insts, which clang
rejects for this target ("not a recognized feature"); the one hardware
dot that assembles (v_dot4_i32_iu8) is UNSIGNEDxUNSIGNED and computes
wrong over signed Q8 activations (verified by device experiment:
byte 0xFD contributed +253x, not -3x). The integer dots therefore ship
as portable scalar loops in the CPU reference's exact accumulation
order — which is what makes the bit-exactness gate achievable. The ISA
dots are recorded as W2 levers with their own exactness arguments.
__shfl_down_sync also requires a 64-bit mask on this target.

Unsupported dtypes (Q4_0/MXFP4, Q8_0-activation) THROW naming the dtype
instead of falling back to a host kernel that cannot follow device
pointers on a discrete card; VT_GGUF_KEEP_QUANT=0 restores load-time
bf16 expansion.

How to verify

On gfx1100 / ROCm 7.14.0 (container rocm-dev:7.14.0):

cmake -B build-hip -S . -GNinja -DCMAKE_BUILD_TYPE=Release \
  -DVLLM_CPP_HIP=ON \
  -DCMAKE_EXE_LINKER_FLAGS="-Wl,-rpath,/opt/rocm/lib -Wl,-rpath-link,/opt/rocm/lib" \
  -DCMAKE_SHARED_LINKER_FLAGS="-Wl,-rpath,/opt/rocm/lib -Wl,-rpath-link,/opt/rocm/lib"
cmake --build build-hip -j 8
ctest --test-dir build-hip -R 'test_rocm_quant_dot|test_gguf_keep_quant|test_ops_quant_dot'
VT_GGUF_KEEP_QUANT=1 ./build-hip/examples/vllm-cli \
  --model <qwen3.5 gguf> --device auto --temperature 0 \
  --max-tokens 64 --prompt "The capital of France is"

Measured on this tree:

What remains unverified or out of scope

  • No fresh independent review yet: this was implemented in-session under
    a recorded deviation (subagent runtime had no model configured). A
    fresh review pass against the spec is still owed before merge.
  • Performance vs the CPU tier is parity-at-these-shapes, not a claimed
    win; the unsigned-dot/dot2 ISA levers are W2 with their own exactness
    arguments.
  • W2 upstream RDNA3 family port + AWQ/GPTQ loader consumer stays owed in
    the spec's ## Owed.
  • docs/ENVIRONMENT.md keep-quant row should gain the ROCm throw caveat;
    riding the follow-up rather than widening this PR.

Closes mudler#1587

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true

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

tbrasser and others added 3 commits August 21, 2026 12:54
…tion call site (mudler#1273)

Closes the "Owed" item from mudler#1065: CheckKvCacheShape's production call site in GPUModelRunner::initialize_kv_cache now has a test. Building the runner with a non-multiple-of-16 block size (kBlockSize = 8) asserts the throw at construction, from the FLASH_ATTN backend's own get_kv_cache_shape — the executable statement of the contract the server's --block-size validation and the bench rounding exist to prevent at the entry points.

Validated on gfx1151 (Strix Halo, ROCm 7.2.3) as part of the mudler#41 M3 battery: test_runner 20/20 cases, 544 assertions, on the current-main build with mudler#1056 + mudler#1065 merged.

Issue: mudler#41

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: DeepSeekV4:Flash [Freebuff]
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]
ghazni101 pushed a commit that referenced this pull request Aug 27, 2026
…ion twice, and the row that nearly replaced this one was refuted (mudler#2061) (mudler#2062)

Opens W11's re-ranked lever #1 as its own campaign, and corrects the
record that
briefly recommended a different one.

THE LEVER. `LagunaFfnBlock`'s grouped-MoE arm issues two `LqGemmGrouped`
calls
over the same activation, one for gate and one for up, and each
re-quantizes it
to Q8_K internally. W11 measured `QuantizeQ8KKernel` at 12.4% of decode
GPU time
and half of that is duplicate. `vt::MoeGateUpSwiGLUGrouped` quantizes
once, runs
both GEMMs and applies the SwiGLU in its epilogue. It is also a
shared-seam
obligation independent of speed, because AGENTS.md routes mergeable MLP
projections through the fused group and Laguna runs two separate ones.

TWO HAZARDS W11 DOES NOT NAME, and the second may bound the row. The
fused op
takes a broadcast activation, so the `Pk x H` staging buffer and its
host copy
loop may disappear as well. And the op requires both expert towers to
share a
block-quant dtype, while `UD-Q4_K_XL` is a DYNAMIC quant that varies
type per
tensor BY DESIGN — so the gate model may be exactly the checkpoint that
cannot
take this path. W1 reads the real tensor table before any code is
written,
because that is a fact about a file rather than something to discover
from a
failing test.

WHAT THIS SPEC CORRECTS. A previous change recommended reworking
Laguna's
host-orchestrated FFN and this row nearly became that. W11 had already
taken a
GO/NO-GO on it and DEMOTED device-residency: after W8 and W9 landed,
GPU-busy
(2.56 s) is approximately the host sync time (2.59 s) is approximately
the decode
wall, so the host is serially WAITING on real kernels rather than idling
between
them, and the rework recovers the ~0.02 s/tok W11 already priced. The
host loops
are real code and were read correctly; the conclusion drawn from them
was stale
by a month. Source inspection identifies candidates and the trace
identifies the
executed path — and here the trace existed before the inference did.

The placement records carried the same error in a second form and are
corrected
in both public documents and the row spec. They said Laguna has no
device-resident expert compute for a placement to move. It HAS
device-resident
expert compute: `LqGemmGrouped` dispatches `vt::MatmulBTQuantGrouped`.
What it
lacks is a `[T,H] -> DBuf` ENTRY, because the FFN boundary is a
per-token host
float row. That is a shape gap, and it is now stated as one — which also
separates Laguna from DeepSeek-V4 and Kimi-Linear, whose experts
genuinely do run
on the host.

The bit-exactness gate is written to refuse a near-tie. Either the fused
arm
reproduces the two-call arm byte for byte, or the row records the
divergence and
stops; 6% is not worth a value change, and the two-call arm stays as the
reference either way.

No number from the W7 spec's llama.cpp denominator is quoted here. `27.8
tok/s`
and every ratio derived from it came from an unrecorded fork branch with
no
commit SHA and are superseded under mudler#1003. This row reports
ours-versus-ours.

Gates: `check-agent-record` ok. `agent-preflight` reports
`audit-live-rows` red,
verified INHERITED rather than caused: `audit-live-rows.py --check`
returns 1 on
clean `origin/main` and identically with this change, and the rows it
names are
ones this change does not touch.

FOLLOWING_AGENTS_PROTOCOL

Following-Agents-Protocol: true
AI-Assisted: true
Assisted-by: Claude:claude-opus-5 [Claude Code]

Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
@ghazni101 ghazni101 closed this Aug 27, 2026
@ghazni101
ghazni101 deleted the row/KERNEL-QUANT-CIQ-GEMM-ROCM branch August 27, 2026 22:23
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.

Port the upstream RDNA3 quantized-GEMM family from csrc/rocm

2 participants