Skip to content

feat: add flash attention support for TQ3_0 K-cache on CUDA/Blackwell#4

Open
marinero2k wants to merge 14 commits into
unixsysdev:mainfrom
marinero2k:main
Open

feat: add flash attention support for TQ3_0 K-cache on CUDA/Blackwell#4
marinero2k wants to merge 14 commits into
unixsysdev:mainfrom
marinero2k:main

Conversation

@marinero2k

Copy link
Copy Markdown

Summary

Implements a flash attention vector kernel for TQ3_0 K-cache, enabling
--cache-type-k tq3_0 --cache-type-v q8_0 with --flash-attn on on
NVIDIA GPUs. Fixes segfault in llama-server when using tq3_0 K-cache.

Tested on RTX 5090 (Blackwell sm_120) — first confirmed working
implementation on this architecture.

Root causes fixed

  1. tq3_0 forced FA off → quantized V-cache required FA → null context → SIGSEGV
  2. Missing GGML_OP_CPY support for F32↔TQ3_0 caused backend scheduler assert
  3. tq3_0 vec cases were inside #ifdef GGML_CUDA_FA_ALL_QUANTS guard (never compiled by default)
  4. Turing/Blackwell path fell through to MMA_F16 kernel which doesn't support tq3_0
  5. K!=V type check rejected tq3_0+q8_0 mixed cache combo

Changes

  • fattn-common.cuh — add vec_dot_fattn_vec_KQ_tq3_0, wired into get_vec_dot_KQ() dispatch
  • fattn.cu — add tq3_0 to K-type switch; vec cases outside FA_ALL_QUANTS guard; force vector kernel for tq3_0 on Turing/Blackwell; allow mixed tq3_0+q8_0/f16 K/V types
  • ggml-cuda.cu — add F32↔TQ3_0 CPY op support
  • CMakeLists.txt — add fattn-vec-instance-tq3_0-{f16,q8_0}.cu instance files
  • llama-context.cpp — remove FA disable for tq3_0 K-cache

Benchmarks

RTX 5090 (sm_120), Qwen3.5-27B Q5_K_M, 200K context:

config pp512 pp4096 tg128 KV VRAM
f16+f16, FA off (baseline) 3646 t/s 3464 t/s 63.28 t/s ~20 GiB
q8_0+q8_0, FA on (previous best) 3682 t/s 3574 t/s 63.42 t/s ~10 GiB
tq3_0+q8_0, FA on (this PR) 3463 t/s 2813 t/s 62.58 t/s ~4.7 GiB
  • Generation speed: -1.3% vs q8_0 baseline ✅
  • KV cache memory: 53% reduction (10 GiB → 4.7 GiB) ✅
  • Prefill at 4K: -21% — vec_dot kernel not yet optimized for prefill ⚠️

Known limitations / future work

  • vec_dot_fattn_vec_KQ_tq3_0 uses simplified dequantization — full WHT inverse not applied in FA hot path (pre-rotate-queries optimization pending)
  • No MMA/tile kernel for tq3_0 — always uses vector kernel path
  • Prefill regression at long contexts due to unoptimized vec_dot

Add vec_dot_fattn_vec_KQ_tq3_0 FA kernel enabling tq3_0 K-cache +
q8_0 V-cache with flash attention enabled on NVIDIA GPUs including
Blackwell sm_120 (RTX 5090).

Changes:
- fattn-common.cuh: add vec_dot_fattn_vec_KQ_tq3_0 and wire into
  get_vec_dot_KQ() dispatch
- fattn.cu: add tq3_0 to K-type switch, vec cases outside FA_ALL_QUANTS
  guard, force vector kernel for tq3_0 on Turing/Blackwell (no MMA),
  allow tq3_0+q8_0/f16 mixed K/V types
- ggml-cuda.cu: add F32<->TQ3_0 CPY op support
- CMakeLists.txt: add fattn-vec-instance-tq3_0-{f16,q8_0}.cu instances
- llama-context.cpp: remove FA disable for tq3_0 K-cache

Benchmarks on RTX 5090 (sm_120), Qwen3.5-27B Q5_K_M, 200K context:

| config              | pp512   | pp4096  | tg128      | KV VRAM |
|---------------------|---------|---------|------------|---------|
| q8_0+q8_0+FA        | 3682    | 3574    | 63.42 t/s  | ~10 GiB |
| tq3_0+q8_0+FA (new) | 3463    | 2813    | 62.58 t/s  | ~4.7 GiB|

Generation speed: -0.84 t/s (-1.3%) vs q8_0 baseline
KV cache memory: 53% reduction (10 GiB → 4.7 GiB)
Prefill at 4K: -21% (vec_dot kernel not yet optimized for prefill)

Fixes segfault when using --cache-type-k tq3_0 with llama-server.
First confirmed working implementation on Blackwell sm_120.
mar added 4 commits March 27, 2026 20:55
Fix infinite reasoning loop caused by incorrect attention scores:
- Use correct centroids {-1.510, -0.4528, 0.4528, 1.510} from ggml-quants.c
- Use correct tq3_signs[32] pattern (seed 42, not alternating 1,-1)
- Pre-rotate Q with WHT forward transform in fattn-vec.cuh before
  quantization, so vec_dot works directly in rotated space
- Remove WHT inverse from hot dot product path (was O(d log d) per call)
- Fix WHT normalization: apply inv_sqrt32 once after butterfly stages

Results on RTX 5090 (sm_120), Qwen3.5-27B Q5_K_M:
- tg128: 61.86 t/s (vs 63.42 baseline, -2.4%)
- No more infinite reasoning loops
- Correct coherent output verified
Add dequantize_V_tq3_0 enabling --cache-type-v tq3_0 with FA.
Fix vec_dot_fattn_vec_KQ_tq3_0 infinite loop bug:
- Correct centroids {-1.510, -0.4528, 0.4528, 1.510}
- Correct tq3_signs[32] from seed 42
- Pre-rotate Q with WHT forward in fattn-vec.cuh
- Fix WHT normalization (once after butterfly, not per stage)

Benchmarks RTX 5090 sm_120, Qwen3.5-27B Q5_K_M, 200K ctx:

| config            | pp512 | pp4096 | tg128      | KV VRAM |
|-------------------|-------|--------|------------|---------|
| q8_0+q8_0 (base)  | 3682  | 3574   | 63.42 t/s  | ~10 GiB |
| tq3_0+q8_0 (v2)   | 3239  | 2670   | 61.86 t/s  | ~4.7 GiB|
| tq3_0+tq3_0 (new) | 1796  | 665    | 58.25 t/s  | ~2.7 GiB|
Replace float centroid multiply with int8 packed dp4a instruction.
int8 centroids {-127, -38, 38, 127} with scale=1.510/127.
Reuses vec_dot_q8_0_q8_1_impl for hardware-accelerated dot product.

Prefill improvement vs float path:
  pp128:  +13 t/s, pp512: +26 t/s, pp1024: +30 t/s, pp4096: +61 t/s
…ster spill

Replace float Q_rot[D=256] (1024 bytes stack, spills to local mem)
with block-by-block WHT processing (32 floats in registers at a time).

Results on RTX 5090 sm_120, tq3_0+q8_0+FA:
  pp512:  3507 t/s (+233 vs dp4a, +7% vs baseline gap closed)
  pp4096: 2855 t/s (+132 vs dp4a)
  tg128:  62.54 t/s (only -1.4% vs q8_0 baseline)
@marinero2k

Copy link
Copy Markdown
Author

Updated Benchmarks (v4 - final)

RTX 5090 (sm_120), Qwen3.5-27B Q5_K_M, 200K context:

config pp512 pp4096 tg128 KV VRAM
q8_0+q8_0+FA (baseline) 3713 3605 63.42 t/s ~10 GiB
tq3_0+q8_0+FA ⭐ recommended 3472 2839 62.52 t/s ~4.7 GiB
tq3_0+tq3_0+FA (max compression) 1928 705 58.84 t/s ~2.7 GiB

tq3_0+q8_0 sweet spot: -1.4% tg, -6.5% pp512, -53% KV VRAM ✅

Optimization journey (4 commits):

  1. Initial FA kernel + segfault fix
  2. Fix math (correct centroids, tq3_signs, pre-rotate Q)
  3. dp4a integer dot product
  4. Block-by-block Q rotation (eliminates register spill)

mar added 2 commits March 27, 2026 21:52
Bug: block-by-block optimization was re-running full WHT32 for every
i0 iteration within the same block (8x per block instead of 1x),
producing garbage attention scores and infinite reasoning loops.

Fix: rotate all D/32 blocks completely first into Q_rot[D],
then quantize. WHT now applied exactly once per block.
Final stable state:
- tq3_0 K + q8_0 V + FA = 62-63 t/s generation
- No reasoning loops
- 53% KV cache reduction (10 GiB -> 4.7 GiB)
- 200K context on single RTX 5090
- Dual GPU: PLANNER(62.78) + BUILDER(62.25) simultaneously = ~125 t/s total
- Verified: coherent output, vision working, OpenCode compatible
@marinero2k

Copy link
Copy Markdown
Author

two models running simultaneously at 125 t/s combined.
preview

TQ3_0V has same block layout as TQ3_0 but skips WHT at quantize/dequantize.
V-cache stored in original space = dequant is just centroid lookup, no WHT.

Results RTX 5090, tq3_0 K + tq3_0v V vs tq3_0 K + q8_0 V:
  d0:     63.89 vs 64.30 t/s (-0.6%)
  d4096:  60.79 vs 61.15 t/s (-0.6%)
  d32768: 54.88 vs 55.43 t/s (-1.0%)
  d65536: 49.27 vs 50.10 t/s (-1.6%)
  KV VRAM: ~2.7 GiB vs ~4.7 GiB (-43% more savings)
  Total KV savings vs q8_0+q8_0: 73%
mar added 6 commits March 28, 2026 10:53
TQ3_0V: V-cache variant stored in original space (no WHT).
Dequant = centroid lookup only, no inverse WHT = massive speedup at depth.

vs tq3_0+tq3_0 (old):
  d0:     63.89 vs 60.25 t/s (+3.6)
  d4096:  60.79 vs 48.02 t/s (+12.7)
  d32768: 54.88 vs 20.11 t/s (+34.7)
  d65536: 49.27 vs ~idle   (+huge)

vs q8_0+q8_0 baseline:
  Speed: -1.6% at d65536
  KV VRAM: 73% reduction (10 GiB -> 2.7 GiB)

Verified: no reasoning loops, coherent output, vision working.
- flash_attn_ext_tq3_0_iter: copies f16 iter, replaces K load with tq3_0 dequant
- flash_attn_ext_tq3_0_process_tile: wires iter with tq3_0v V support
- tq3_0_load_tile: dequantizes K (WHT inverse + centroid)
- tq3_0v_load_tile: dequantizes V (centroid only, no WHT) - BUG: stride wrong
- Dispatch: currently forced to VEC pending V dequant fix
- TODO: fix stride_V_tq3 propagation through process_tile to iter
- TODO: verify tq3_0v centroid extraction correctness
- Fix dispatch: return MMA_TQ3_0 instead of VEC for D=128,256 (5.5x pp speedup)
- Fix is_fixup stride: pass V_is_tq3_0v ? stride_V_tq3 : stride_V (fixes garbage output)
- Optimize dequant: LUT centroid lookup, vectorized uint32 extraction (no branches)
- tg128: 60.5 t/s (98% of f16), pp4096: 5330 t/s (98.7% of f16)
- FIX 1+2: add tq3_0v branch in nstages<=1 V load path (was always using f16 loader)
- FIX 3: add V_is_tq3_0v to last_iter template calls (was defaulting to false)
- All 4 original V dequant bugs now fixed, coherent output confirmed with -ctk tq3_0 -ctv tq3_0v
…kernel

Earlier sed accidentally replaced BEST_FATTN_KERNEL_VEC with MMA_TQ3_0 in
non-tq3_0 dispatch paths, causing f16/q8_0 single-token attention to run
through the tq3_0 kernel. This corrupted attention scores and caused
model output degradation (reasoning loops, incoherent output).

Now only K->type == GGML_TYPE_TQ3_0 returns MMA_TQ3_0. All other paths
correctly return VEC/MMA_F16 as upstream.
@c00jsw00

Copy link
Copy Markdown

after compling the code and ran, the error still occured:
(openclaw) c00jsw00@amdyt:~/models$ /home/c00jsw00/models/llama-turboquant/build-hip/bin/llama-server -m Qwen3.5-35B-A3B-UD-Q8_K_XL.gguf --cache-type-k tq3_0 --port 8080
error while handling argument "--cache-type-k": Unsupported cache type: tq3_0

usage:
-ctk, --cache-type-k TYPE KV cache data type for K
allowed values: f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1,
▒▒k▒&s
(default: f16)
(env: LLAMA_ARG_CACHE_TYPE_K)

to show complete usage, run with -h

@marinero2k

marinero2k commented Mar 31, 2026 via email

Copy link
Copy Markdown
Author

@marinero2k

marinero2k commented Mar 31, 2026 via email

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants