Skip to content

fix: MSE_BITS==8 OOB kernel patches + FORCE_SYNC removal for spec-TQ84 - #1

Open
amd-dlimpus wants to merge 4 commits into
feat/spec-tq84from
feat/spec-tq84-kernel-fix
Open

fix: MSE_BITS==8 OOB kernel patches + FORCE_SYNC removal for spec-TQ84#1
amd-dlimpus wants to merge 4 commits into
feat/spec-tq84from
feat/spec-tq84-kernel-fix

Conversation

@amd-dlimpus

Copy link
Copy Markdown
Owner

feat: spec-TQ84 — 8-bit Lloyd-Max KV codec with cudagraph fix and OOB kernel patches

Base branch: feat/spec-tq84
Target branch: feat/spec-tq84-kernel-fix
Remote: origin (aditi-amd/vllm)


Summary

Spec-TQ84 is an 8-bit Lloyd-Max quantized KV cache codec for vLLM, implementing the approach from arXiv 2504.19874. It uses 256-centroid Lloyd-Max codebooks with online Hadamard rotation and per-token L2 norm preservation to quantize KV cache keys to 8 bits while maintaining distributional fidelity.

The upstream TQ84 path used a bare FP8 cast with no rotation or codebook optimization, causing distributional mismatch and accuracy degradation. Spec-TQ84 closes this gap: at cap=16384, LCB multi-turn accuracy reaches 0.654 vs TQ44 at 0.372 (delta +28pp, McNemar p=1.17e-4).

This PR (feat/spec-tq84-kernel-fix) contains three targeted fixes on top of feat/spec-tq84:

  1. A critical OOB memory read in the Triton decode kernel for MSE_BITS==8
  2. The same fix ported to the v2 decode kernel
  3. Removal of the VLLM_TQ_FORCE_SYNC workaround that was masking the OOB bug

Changes in this branch

Commit Description
5692d0fbd fix: add MSE_BITS==8 branch to _tq_decode_stage1 to fix cudagraph OOB read
542d61ad8 cleanup: remove VLLM_TQ_FORCE_SYNC workaround (superseded by 5692d0fbd)
7d3f8f495 fix: add MSE_BITS==8 branch to v2 decode kernel (same OOB as 5692d0fbd)

Files changed: 3 files, +44 / -61 lines (net -17)


Bug fixes (critical)

MSE_BITS==8 OOB kernel read (5692d0fbd, 7d3f8f495)

_tq_decode_stage1 (v1) and _tq_decode_stage1_v2 (v2) were both missing a 1-byte-per-element fast path for 8-bit MSE keys (spec-TQ84 uses 256 centroids = 8 bits per index).

The generic raw16 bit-extraction path loads two consecutive bytes (mse_addrs0 and mse_addrs0 + 1) and combines them into a 16-bit word before shifting. For MSE_BITS==8, the second byte load at d = D-1 reads one byte past the end of the MSE region. Under eager dispatch this OOB read happened to land on valid (but wrong) memory and was masked. Under cudagraph replay, the same address lands on an unmapped page, causing a HIP memory fault:

Memory access fault by GPU node-1 (Agent handle: 0x...) on address 0x...

Fix: When MSE_BITS == 8, use a direct single-byte load per element (no bit-packing, no raw16 combine). This is both correct and faster for the 8-bit case.

The same fix was already applied to _tq_full_dequant_kv and _tq_load_k_tile in commit b87671809 on the parent branch; _tq_decode_stage1 (v1 and v2) were the last missed sites.

VLLM_TQ_FORCE_SYNC removal (542d61ad8)

The VLLM_TQ_FORCE_SYNC environment variable enabled a per-dispatch cuda.synchronize() workaround that was introduced to mask the OOB fault above. With the root cause fixed, this dead code path adds ~200us serialization overhead per decode step when enabled and serves no purpose. Removed from both triton_turboquant_decode.py and triton_turboquant_unified_attention.py.


Preset

The parent branch (feat/spec-tq84) introduces the turboquant_k8v4_spec_nc preset:

  • Keys: 8-bit Lloyd-Max MSE (256 centroids), Hadamard rotation, per-token fp16 L2 norm
  • Values: 4-bit Lloyd-Max

This PR does not modify the preset; it only fixes the kernel to correctly decode 8-bit MSE keys.


Test results

Suite Result
Total tests 513/531 pass
spec-TQ84 round-trip tests (6) All PASS
Multi-step decode (128 tokens, cudagraph, no --enforce-eager) PASS
Known failures (18) 11 test expectation staleness, 7 CPU LAPACK env gap

Branch relationship

  • feat/spec-tq84-kernel-fix is 3 commits ahead of feat/spec-tq84
  • feat/spec-tq84-kernel-fix is 0 commits behind feat/spec-tq84
  • Merge base = tip of feat/spec-tq84 (722397c0b)
  • Clean fast-forward merge — no rebase or conflict resolution needed
  • feat/spec-tq84 must be merged to the main integration branch first; then feat/spec-tq84-kernel-fix can fast-forward merge on top

Known limitations

  • cap=16384 McNemar result has cap-asymmetry caveat: Baselines were evaluated at cap=4096; cap=4096 is too tight for reasoning models. Cross-cap comparison is directional, not apples-to-apples.
  • Residual server instability at very long runs (75+ min): Root cause under investigation; not related to this PR.

Review checklist

  • Confirm MSE_BITS==8 direct-load path produces identical output to Python reference decode
  • Verify cudagraph replay with turboquant_k8v4_spec_nc preset (no --enforce-eager)
  • Confirm VLLM_TQ_FORCE_SYNC removal does not regress non-TQ84 configs (TQ44, TQ34)

amd-dlimpus and others added 4 commits May 18, 2026 20:10
… read

_tq_decode_stage1 was missing the 1-byte-per-element fast path for 8-bit
MSE keys (spec-TQ84). The generic raw16 path read mse_addrs0+1, which is
one byte past the MSE region for d=D-1. Under cudagraph, this OOB read
lands on an unmapped page causing a HIP memory fault.

Same fix already applied to _tq_full_dequant_kv and _tq_load_k_tile (v3)
in commit b876718. This closes the last missed site.

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
The per-dispatch cuda sync was masking an OOB read in _tq_decode_stage1
for MSE_BITS==8. Commit 5692d0f fixed the root cause. The workaround
is now dead code and adds ~200us serialization overhead per decode step
when enabled.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
triton_turboquant_decode_v2.py had the same missing 1-byte-per-element
path as _tq_decode_stage1 in the v1 kernel. The raw16 path reads
mse_addrs0+1, causing OOB for MSE_BITS==8 (spec-TQ84). v2 is opt-in
(VLLM_TQ_DECODE_V2=1) but must be safe before upstreaming.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
Add turboquant_k8v4_nc and turboquant_k8v4_spec_nc to PRESET_EXPECTED.
Update key_quant_bits exclusivity assertion to include 8 (MSE 8-bit keys
used by spec-TQ84).

Co-Authored-By: Claude Sonnet 4 <noreply@anthropic.com>
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