Skip to content

Commit 9ae8703

Browse files
maeehartcursoragent
andcommitted
[ROCm][DSv4] Tighten KV-workspace zero comment + FP8 encoding notes (review #42893)
Replace the long rationale around ``kv.zero_()`` (in both prefill paths) with a brief TODO that names the proper fix: mask invalid rows in the indexer (score = -inf) or in the sparse-attention kernel (skip indices >= valid_len). The current zero is the minimal interim workaround; the underlying bug is arch-independent (uninitialized workspace + indexer that scores the entire M dim) so the call stays unchanged on every platform until the indexer/kernel fix lands. No behavior change. Also condense the duplicate FNUZ-vs-OCP comments at the dequant call sites and in ``_sparse_attn_decode_ragged_kernel``: the wrapper docstring already explains the asymmetry, so per-call-site repetition was just noise. Signed-off-by: Markus Hartikainen <markus.hartikainen@amd.com> Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent f13e445 commit 9ae8703

2 files changed

Lines changed: 12 additions & 35 deletions

File tree

vllm/models/deepseek_v4/amd/rocm.py

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -790,17 +790,10 @@ def _forward_prefill(
790790
kv = workspace_manager.get_simultaneous(
791791
((cls.PREFILL_CHUNK_SIZE, M, q.shape[-1]), torch.bfloat16),
792792
)[0]
793-
# The workspace allocator returns uninitialized memory and is shared
794-
# across requests + other layers. dequantize_and_gather_k_cache only
795-
# writes the compressed-K prefix (rows [0, seq_len/compress_ratio))
796-
# and the SWA window (rows [N, N+gather_lens)) for each chunk row,
797-
# leaving holes in the M dimension. rocm_sparse_attn_prefill then
798-
# reads ``kv.view(-1, 1, head_dim)`` via ragged indices which can
799-
# reach those holes for very short sequences, causing data-dependent
800-
# non-determinism across otherwise-identical temperature=0 requests.
801-
# Zero once per call so the holes are deterministic (zero attention
802-
# contribution). The cost is one bf16 fill of the workspace tile,
803-
# which is dwarfed by the FP8 dequant + sparse attention themselves.
793+
# TODO: workspace is torch.empty() and only the compressed-K prefix +
794+
# SWA window are written per chunk row; the indexer's topK can land in
795+
# the unwritten holes for short sequences. Proper fix is to mask invalid
796+
# rows in the indexer (score = -inf) or in rocm_sparse_attn_prefill.
804797
kv.zero_()
805798
for chunk_idx in range(num_chunks):
806799
chunk_start = chunk_idx * cls.PREFILL_CHUNK_SIZE
@@ -810,12 +803,7 @@ def _forward_prefill(
810803
assert attn_metadata is not None
811804
assert compressed_k_cache is not None
812805
block_table = attn_metadata.block_table[num_decodes:]
813-
# The compressed-K encoder (Triton _fused_kv_compress_norm_...
814-
# in fused_compress_quant_cache.py) writes bytes via
815-
# tl.float8e4nv with FP8_MAX=448.0 regardless of platform.
816-
# The SWA-side C++ encoder, by contrast, switches to FNUZ on
817-
# gfx942 (PR #42893), so the two caches need different
818-
# use_fnuz settings even on the same MI300X.
806+
# compressed_k_cache is OCP on every platform (Triton encoder).
819807
dequantize_and_gather_k_cache(
820808
kv[:chunk_size],
821809
compressed_k_cache,

vllm/v1/attention/ops/rocm_aiter_mla_sparse.py

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -565,16 +565,11 @@ def rocm_fp8_mqa_logits(
565565

566566
k_fp8, scale = kv
567567

568-
# gfx942 (MI300X): the AITER ``fp8_mqa_logits`` wrapper bundled in the
569-
# currently-pinned aiter wheel launches its Triton kernel with
570-
# ``(BLOCK_KV=128, num_stages=2)``, which requests ~96 KiB of LDS for
571-
# the DSv4 sparse indexer shape. MI300X CUs have 64 KiB of LDS, so
572-
# the launch JIT-aborts with ``OutOfResources: shared memory`` on the
573-
# first inference. Route gfx942 callers to a vLLM-vendored copy of
574-
# the same kernel that selects ``(BLOCK_KV=64, num_stages=1)`` when
575-
# the default tile doesn't fit (~33 KiB), matching the fix in
576-
# ROCm/aiter#3257. This entire branch can be removed once vLLM bumps
577-
# to an AITER version that includes that PR.
568+
# gfx942: AITER's bundled fp8_mqa_logits launches with BLOCK_KV=128 +
569+
# num_stages=2 (~96 KiB LDS), exceeding MI300X's 64 KiB LDS so it aborts
570+
# with OutOfResources. Route gfx942 to a vendored copy that drops to
571+
# BLOCK_KV=64 + num_stages=1 (~33 KiB) per ROCm/aiter#3257. Remove this
572+
# branch once vLLM bumps AITER to a version that includes that PR.
578573
if _ON_GFX942 and rocm_aiter_ops.is_enabled():
579574
from vllm.v1.attention.ops.triton_fp8_mqa_logits import (
580575
fp8_mqa_logits_gfx942,
@@ -1193,10 +1188,8 @@ def _sparse_attn_decode_ragged_kernel(
11931188
NOPE_DIM: tl.constexpr,
11941189
NOPE_BLOCK: tl.constexpr,
11951190
ROPE_DIM: tl.constexpr,
1196-
# `main_cache` is the SWA K-cache (written by the C++ encoder, FNUZ on
1197-
# gfx942 / OCP on gfx950). `extra_cache` is the compressed K-cache
1198-
# (Triton encoder, OCP on every platform). Reading both with the same
1199-
# `IS_FNUZ` would mis-decode one of them by the FNUZ/OCP scale ratio.
1191+
# SWA K-cache (main): C++ encoder writes FNUZ on gfx942, OCP on gfx950.
1192+
# Compressed K-cache (extra): Triton encoder writes OCP everywhere.
12001193
IS_FNUZ_MAIN: tl.constexpr,
12011194
IS_FNUZ_EXTRA: tl.constexpr,
12021195
BLOCK_H: tl.constexpr,
@@ -1601,10 +1594,6 @@ def _rocm_sparse_attn_decode_ragged_triton(
16011594
NOPE_DIM=nope_head_dim,
16021595
NOPE_BLOCK=triton.next_power_of_2(nope_head_dim),
16031596
ROPE_DIM=rope_head_dim,
1604-
# main_cache = swa_k_cache (C++ encoder, FNUZ on gfx942 / OCP on gfx950).
1605-
# extra_cache = compressed kv_cache (Triton encoder, OCP everywhere).
1606-
# Reading both with a single IS_FNUZ would mis-decode one of them by
1607-
# the FNUZ/OCP scale ratio (~1.87×).
16081597
IS_FNUZ_MAIN=current_platform.is_fp8_fnuz(),
16091598
IS_FNUZ_EXTRA=False,
16101599
BLOCK_H=block_h,

0 commit comments

Comments
 (0)