[ROCm] Extend the gfx950 extend-attention tile to head_dim <= 128: -43% kernel, -14% TTFT, bit-identical (#34461)
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c439e77872
commit
666b08b4a5
@@ -80,12 +80,11 @@ def _get_block_sizes_for_extend_attention(Lq: int, Lv: int):
|
|||||||
# late-prefill kernel from ~12.57 ms to ~5.24 ms.
|
# late-prefill kernel from ~12.57 ms to ~5.24 ms.
|
||||||
BLOCK_M, BLOCK_N = (64, 32)
|
BLOCK_M, BLOCK_N = (64, 32)
|
||||||
num_warps = 4
|
num_warps = 4
|
||||||
elif _is_gfx95 and 128 < Lq <= 256:
|
elif _is_gfx95 and Lq <= 256:
|
||||||
# gfx950 (CDNA4), 128 < head_dim <= 256: a larger query tile halves KV bytes
|
# gfx950 (CDNA4), head_dim <= 256: every workgroup streams the whole
|
||||||
# streamed per call (each workgroup reads the whole prefix); 8 warps
|
# prefix, so a larger query tile halves the KV bytes read per call;
|
||||||
# hide the loads. Measured on MI350X head_dim 256: -36% kernel time,
|
# BLOCK_M / num_warps = 16 rows per warp is exactly one MFMA tile at
|
||||||
# 28% -> 44% MFU, numerically equivalent (BLOCK_N reduction order
|
# matrix_instr_nonkdim=16. Measured on MI350X at head_dim 64, 128, 256.
|
||||||
# unchanged). Other AMD archs / head dims keep the default below.
|
|
||||||
BLOCK_M, BLOCK_N = (128, 64)
|
BLOCK_M, BLOCK_N = (128, 64)
|
||||||
num_warps = 8
|
num_warps = 8
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -326,22 +326,24 @@ class TestTritonAttention(CustomTestCase):
|
|||||||
|
|
||||||
if not ea._is_hip:
|
if not ea._is_hip:
|
||||||
self.skipTest("HIP-only block-size selection")
|
self.skipTest("HIP-only block-size selection")
|
||||||
# head_dim <= 128 keeps the default config on all HIP archs
|
# head_dim <= 256: tuned tile on gfx95, default elsewhere. 64 is gpt-oss,
|
||||||
self.assertEqual(
|
# 128 is the llama/qwen family, 256 is gemma -- all measured on MI350X.
|
||||||
ea._get_block_sizes_for_extend_attention(128, 128)[3:], (64, 64, 4)
|
|
||||||
)
|
|
||||||
# 128 < head_dim <= 256: tuned tile on gfx95, default elsewhere
|
|
||||||
expected = (128, 64, 8) if ea._is_gfx95 else (64, 64, 4)
|
expected = (128, 64, 8) if ea._is_gfx95 else (64, 64, 4)
|
||||||
self.assertEqual(
|
for head_dim in (64, 128, 256):
|
||||||
ea._get_block_sizes_for_extend_attention(256, 256)[3:], expected
|
self.assertEqual(
|
||||||
)
|
ea._get_block_sizes_for_extend_attention(head_dim, head_dim)[3:],
|
||||||
|
expected,
|
||||||
|
)
|
||||||
# head_dim > 256 falls back to the default unless the automatic
|
# head_dim > 256 falls back to the default unless the automatic
|
||||||
# Triton-3.7 gfx950 Lq=576/Lv=512 spill workaround applies.
|
# Triton-3.7 gfx950 Lq=576/Lv=512 spill workaround applies.
|
||||||
|
self.assertEqual(
|
||||||
|
ea._get_block_sizes_for_extend_attention(576, 576)[3:], (64, 64, 4)
|
||||||
|
)
|
||||||
with unittest.mock.patch.object(ea, "_is_triton_ge_37", True):
|
with unittest.mock.patch.object(ea, "_is_triton_ge_37", True):
|
||||||
expected = (64, 32, 4) if ea._is_gfx95 else (64, 64, 4)
|
expected_spill = (64, 32, 4) if ea._is_gfx95 else (64, 64, 4)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
ea._get_block_sizes_for_extend_attention(576, 512)[3:],
|
ea._get_block_sizes_for_extend_attention(576, 512)[3:],
|
||||||
expected,
|
expected_spill,
|
||||||
)
|
)
|
||||||
with unittest.mock.patch.object(ea, "_is_triton_ge_37", False):
|
with unittest.mock.patch.object(ea, "_is_triton_ge_37", False):
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
|||||||
Reference in New Issue
Block a user