Skip to content

vulkan : multi-row coopmat1 flash attention for prefill, with a V^T scratch - #159

Merged
dzannotti merged 1 commit into
halo-box:masterfrom
Nathanw1014:hb/vk-fa-mr
Oct 8, 2026
Merged

dzannotti merged 1 commit into
halo-box:masterfrom
Nathanw1014:hb/vk-fa-mr

Conversation

@Nathanw1014

Copy link
Copy Markdown

This adds a coopmat1 prefill flash attention kernel that processes several query rows per subgroup, plus a transposed V scratch so the P x V product reads V in the layout the matrix cores want. It is the default for head size 256 on RADV RDNA3. The plain cm1 kernel also uses the transposed V on RADV RDNA3 when the NaN-safe path is off. Ported from the nathanw fork (v0.7.8), with the masked-V zeroing from the -0.0 fix built into the new kernel.

On a Strix Halo (gfx1151, RADV, Mesa 26.2.4), pp2048 -ub 2048, same binary with GGML_VK_FA_MR=0 / GGML_VK_FA_VT=0 as the off arm:

model depth off on change
Qwen3.8-27B UD-Q4_K_XL 32k 296.4 345.7 +16.6%
Qwen3.6-35B-A3B UD-Q4_K_XL 32k 1135.3 1461.0 +28.7%
Qwen2.5-7B Q4_K_M (hs128, V^T only) 8k 1162.2 1239.3 +6.6%
Qwen3.8-Flash-Next REAP-320 0 / 8k / 16k within noise

Against current master the 27B goes from 284 to 346 t/s at 32k, and the 35B-A3B from about 1068 to 1461. Depth 0 is unchanged.

  • New flag bits: 64 V_TRANSPOSED, 128 MULTI_ROW, 256/512/1024 for the kernel's options. NAN_SAFE_V stays at 32.
  • The kernel declines a shape unless the mask is per-token with a KV×2 stride, the subgroup clustered and vote ops are present, V strides are 8-byte aligned, KV < 4M, and the V^T scratch fits the max buffer size.
  • Switches: GGML_VK_FA_MR=0 turns it off and GGML_VK_FA_MR=1 enables it for head size 128 too. GGML_VK_FA_VT=0 turns off the transposed V. GGML_VK_FA_MR_LOG=1 logs each dispatch.
  • Tests: new FLASH_ATTN_EXT cases for head size 256 and 128 with GQA 2 to 12, sinks, ALiBi, softcap, q8_0 KV and the dense KV-cache layout. 5359/5359 pass with the kernel on, forced on for head size 128, off, with V^T off, and with each option off.
  • KLD against the kernel switched off, Qwen3.8-27B, 2 chunks of 4096: 0.00036, same top token 99.0%.

Written with Claude, tested by me.

Assisted-by: Claude Opus 5.5

…cratch

flash_attn_cm1_mr.comp: one workgroup of eight wave32 subgroups owns 64
rows (H heads x 64/H tokens that share a KV head). Q stays in registers
for the whole KV loop and every 16-key K/V tile serves all 64 rows,
where cm1 pins Br at 16 and streams the whole K/V range per 16 rows.
K is read as per-head f16 rows, V from a per-head V^T scratch written by
dequant_f16_transpose_vt.comp ([HSV][KV + 128], 64x64 LDS tiles).
Keys masked in every output row have their V zeroed per tile, so a
WMMA -0.0 product cannot move the output and NaN V in masked cells
cannot leak.

GGML_VK_FA_MR: default on for head size 256 on RADV RDNA3, =1 for every
eligible shape (head size 128 or 256, f16 K after the contiguize pass,
f32 accumulation, KV % 64 == 0, mask shared across heads), =0 off.
GGML_VK_FA_MR_LAZY / _NOEB / _VPOST (default on) are schedule knobs.
GGML_VK_FA_VT: V^T for the cm1 kernel too (default on RADV RDNA3 when
NAN_SAFE_V and shmem staging are off), =0 also disables MR.
New flag bits 64..1024; NAN_SAFE_V keeps 32.

Measured in the fork (v0.7.8) on gfx1151, Qwen3.8-27B UD-Q4_K_XL:
FA per pp2048 ubatch at d32768 2067 -> 954 ms, pp2048 +21.5% at d32768,
+5.7% at d8192, KLD 0.000197 against the cm1 kernel.

Assisted-by: Claude Opus 5.5
@dzannotti
dzannotti merged commit 0cb11d9 into halo-box:master Oct 8, 2026
7 of 8 checks passed
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