A high-throughput and memory-efficient inference and serving engine for LLMs
Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git
Synced Nov 3, 22:45 UTC
[ROCm] Cap Triton paged attention block size to fix ROCm shared memory OOM (#38502)
Signed-off-by: Andreas Karatzas <akaratza@amd.com>
Andreas Karatzas committed
May 10, 2026 at 05:03 UTC
0a309b5ee9480d0d78e7f00946a418ffd7e3baf1
3 files changed
+44
-18
.buildkite/test-amd.yaml
+9
-6
@@ -1803,9 +1803,10 @@ steps:
1803
- tests/models/multimodal/generation
1804
- tests/models/multimodal/test_mapping.py
1805
commands:
1806
- - pip install git+https://github.com/TIGER-AI-Lab/Mantis.git
1807
- - pytest -v -s models/multimodal/generation -m 'not core_model' --ignore models/multimodal/generation/test_common.py
1808
- - pytest -v -s models/multimodal/test_mapping.py
1806
+ - uv pip install --system --no-build-isolation 'git+https://github.com/AndreasKaratzas/mamba@rocm-7.0-v2.3.0'
1807
+ - uv pip install --system --no-build-isolation 'git+https://github.com/Dao-AILab/causal-conv1d@v1.6.0'
1808
+ - pytest -v -s models/language/generation -m hybrid_model --num-shards=$$BUILDKITE_PARALLEL_JOB_COUNT --shard-id=$$BUILDKITE_PARALLEL_JOB
1809
+
1810
1811
- label: Multi-Modal Models (Extended Generation 2) # TBD
1812
timeout_in_minutes: 180
@@ -1817,8 +1818,10 @@ steps:
1818
- vllm/
1819
- tests/models/multimodal/generation
1820
commands:
1820
- - pip install git+https://github.com/TIGER-AI-Lab/Mantis.git
1821
- - pytest -v -s models/multimodal/generation/test_common.py -m 'split(group=0) and not core_model'
1821
+ - uv pip install --system --no-build-isolation 'git+https://github.com/AndreasKaratzas/mamba@rocm-7.0-v2.3.0'
1822
+ - uv pip install --system --no-build-isolation 'git+https://github.com/Dao-AILab/causal-conv1d@v1.6.0'
1823
+ - pytest -v -s models/language/generation -m '(not core_model) and (not hybrid_model)'
1824
+
1825
1826
- label: Multi-Modal Models (Extended Generation 3) # TBD
1827
timeout_in_minutes: 180
@@ -3043,7 +3046,7 @@ steps:
3046
- vllm/
3047
- tests/models/language/generation
3048
commands:
3046
- - uv pip install --system --no-build-isolation 'git+https://github.com/AndreasKaratzas/mamba@fix-rocm-7.0-warp-size-constexpr'
3049
+ - uv pip install --system --no-build-isolation 'git+https://github.com/AndreasKaratzas/mamba@rocm-7.0-v2.3.0'
3050
- uv pip install --system --no-build-isolation 'git+https://github.com/Dao-AILab/causal-conv1d@v1.6.0'
3051
- pytest -v -s models/language/generation -m '(not core_model) and (not hybrid_model)'
3052
vllm/v1/attention/backends/rocm_attn.py
+8
-4
@@ -29,6 +29,7 @@ from vllm.v1.attention.backend import (
29
)
30
from vllm.v1.attention.ops.chunked_prefill_paged_decode import (
31
chunked_prefill_paged_decode,
32
+ has_native_kv_cache_layout,
33
)
34
from vllm.v1.attention.ops.paged_attn import PagedAttention
35
from vllm.v1.attention.ops.triton_reshape_and_cache_flash import (
@@ -468,9 +469,10 @@ class RocmAttentionImpl(AttentionImpl):
469
# Get the actual block_size from value_cache
470
# value_cache shape: [num_blocks, num_heads, head_size, block_size]
471
block_size = value_cache.shape[3]
472
+ has_native_layout = has_native_kv_cache_layout(key_cache, value_cache)
473
472
- if block_size in (16, 32):
473
- # Normal 16, 32, use vLLM native HIP C++ logic
474
+ if block_size in (16, 32) and has_native_layout:
475
+ # Normal 16, 32 with contiguous blocks: use vLLM native HIP C++ logic.
476
PagedAttention.write_to_paged_cache(
477
key,
478
value,
@@ -482,8 +484,10 @@ class RocmAttentionImpl(AttentionImpl):
484
layer._v_scale,
485
)
486
else:
485
- # Case B: Non-standard blocks (e.g., 64, 128, 544 in Qwen3Next or Qwen3.5 ),
486
- # force using our modified Triton logic
487
+ # Non-standard blocks and hybrid attention/Mamba layouts need the
488
+ # stride-aware Triton writer. The native reshape_and_cache kernel
489
+ # assumes contiguous block storage and writes to the wrong hybrid
490
+ # cache blocks.
491
triton_reshape_and_cache_flash(
492
key,
493
value,
vllm/v1/attention/ops/chunked_prefill_paged_decode.py
+27
-8
@@ -21,6 +21,22 @@ logger = init_logger(__name__)
21
float8_info = torch.finfo(current_platform.fp8_dtype())
22
23
24
+def has_native_kv_cache_layout(
25
+ key_cache: torch.Tensor,
26
+ value_cache: torch.Tensor,
27
+) -> bool:
28
+ """Return whether KV cache blocks can use the native ROCm pairing.
29
+
30
+ The native reshape_and_cache writer assumes packed blocks. If cache update
31
+ needs reshape_and_cache_flash for a stride-padded hybrid layout, decode
32
+ should use the matching Triton path too.
33
+ """
34
+ return (
35
+ key_cache.stride(0) == key_cache.shape[1:].numel()
36
+ and value_cache.stride(0) == value_cache.shape[1:].numel()
37
+ )
38
+
39
+
40
@triton.jit
41
def cdiv_fn(x, y):
42
return (x + y - 1) // y
@@ -346,14 +362,12 @@ def chunked_prefill_paged_decode(
362
alibi_slopes,
363
sinks,
364
)
349
- # Triton is only forced when encountering a non-standard block
350
- # like Qwen3 with a size of 544.
351
- # 1. Check if block_size is a power of 2 (16, 32, 64...)
352
- # 2. If it's a power of 2, we trust the vLLM's native use_custom decision.
353
- # 3. If it's not a power of 2 (such as Qwen3's 544),
354
- # then our Triton path is forced.
365
+ has_native_layout = has_native_kv_cache_layout(key_cache, value_cache)
366
+ # Force Triton for non-standard blocks like Qwen3's 544 and for
367
+ # stride-padded hybrid layouts. The latter use reshape_and_cache_flash
368
+ # during cache update, so keep decode on the matching stride-aware path.
369
is_pow2 = block_size > 0 and (block_size & (block_size - 1) == 0)
356
- if not is_pow2:
370
+ if not is_pow2 or not has_native_layout:
371
use_custom = False
372
373
if use_custom:
@@ -404,7 +418,12 @@ def chunked_prefill_paged_decode(
418
real_block_size = value_cache.shape[3]
419
# The standard model directly uses the original block_size.
420
# Non-standard 544 uses 32 to accommodate integer division logic.
407
- TRITON_BLOCK_SIZE = block_size if is_pow2 else 32
421
+ # Cap at 128 to avoid exceeding GPU shared memory limits
422
+ # (e.g. hybrid Mamba models inflate block_size to 2048).
423
+ # The kernel handles TRITON_BLOCK_SIZE != PHYSICAL_BLOCK_SIZE
424
+ # via the l_block_idx/internal_offsets addressing logic.
425
+ MAX_TRITON_BLOCK_SIZE = 128
426
+ TRITON_BLOCK_SIZE = min(block_size, MAX_TRITON_BLOCK_SIZE) if is_pow2 else 32
427
if is_block_table_ptr:
428
# Using the physical base address of tensors
429
kv_element_size = key_cache.element_size()