Skip to content

Commit fd76d65

Browse files
committed
feat(sm12x): default packed prefill on when SM120 FI backend active
wingcomm's external nc=8 + MTP validation (PR vllm-project#41834: GSM8K 0.977 at genuine max_num_seqs=8, 0 errors, no OOM; the 0.90 that shelved packed does not reproduce) clears packed prefill for promotion. Flip VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL default OFF -> ON so the packed FlashInfer SM120 sparse-MLA prefill runner is used by default whenever its backend is active, instead of requiring a second opt-in flag on top of VLLM_DEEPSEEK_V4_FLASHINFER_SM120_DECODE. Safety: the prefill-SWA index kernel is launched by the SHARED DeepseekSparseSWAMetadataBuilder, which previously gated only on the (default-off) PREFILL flag. With the flag now default-on that gate alone would fire the kernel on the default FlashMLA/Triton path and fault (cudaErrorLaunchFailure). Add is_dsv4_sm120_fi_prefill_active() (DECODE opted in + SM12x + FI SM120 kernel present) and require it in both the builder gate and implicitly via the decode backend's _forward_prefill (only reached when that backend is selected). Default FlashMLA path, non-SM12x, kernel-absent builds, and explicit =0 are byte-for-byte unchanged. Adds a unit test for the gate's AND logic. Not yet runtime-confirmed on our boxes (GB10 offline; RTX GPU0 wedged + flashinfer 0.6.11 lacks the kernel) -- staged on a feature branch, unpushed, pending a box that can serve the SM120 FI backend. Signed-off-by: jasl <jasl9187@hotmail.com>
1 parent 2a9a806 commit fd76d65

4 files changed

Lines changed: 105 additions & 8 deletions

File tree

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3+
"""Unit tests for the DSv4 SM120 packed-prefill backend-active gate.
4+
5+
``is_dsv4_sm120_fi_prefill_active()`` is the signal the SHARED
6+
``DeepseekSparseSWAMetadataBuilder`` uses to decide whether to launch the
7+
prefill-SWA index kernel. Since ``VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL`` now
8+
defaults ON, this gate is what keeps the default FlashMLA/Triton path from
9+
launching that kernel (which faults). It must be True ONLY when all three of
10+
{DECODE opted in, SM12x device, FI SM120 kernel present} hold.
11+
"""
12+
from unittest.mock import patch
13+
14+
import pytest
15+
16+
from vllm.utils import flashinfer
17+
18+
19+
@pytest.fixture(autouse=True)
20+
def _clear_cache():
21+
flashinfer.is_dsv4_sm120_fi_prefill_active.cache_clear()
22+
yield
23+
flashinfer.is_dsv4_sm120_fi_prefill_active.cache_clear()
24+
25+
26+
def _run(monkeypatch, decode: bool, family120: bool, kernel: bool) -> bool:
27+
# The DECODE env resolves through os.environ via the envs.py getter lambda.
28+
monkeypatch.setenv(
29+
"VLLM_DEEPSEEK_V4_FLASHINFER_SM120_DECODE", "1" if decode else "0"
30+
)
31+
with (
32+
patch(
33+
"vllm.platforms.current_platform.is_device_capability_family",
34+
return_value=family120,
35+
),
36+
patch.object(
37+
flashinfer,
38+
"has_flashinfer_trtllm_sparse_mla_dsv4",
39+
return_value=kernel,
40+
),
41+
):
42+
flashinfer.is_dsv4_sm120_fi_prefill_active.cache_clear()
43+
return flashinfer.is_dsv4_sm120_fi_prefill_active()
44+
45+
46+
def test_all_three_true_is_active(monkeypatch):
47+
assert _run(monkeypatch, decode=True, family120=True, kernel=True) is True
48+
49+
50+
@pytest.mark.parametrize(
51+
"decode,family120,kernel",
52+
[
53+
(False, True, True), # SM120 FI decode backend not opted in (the default)
54+
(True, False, True), # not SM12x (e.g. Hopper)
55+
(True, True, False), # FI SM120 kernel absent (flashinfer < 0.6.13)
56+
(False, False, False),
57+
],
58+
)
59+
def test_any_condition_false_is_inactive(monkeypatch, decode, family120, kernel):
60+
# Safety-critical: DECODE off (the default) -> inactive, so the shared SWA
61+
# builder must NOT launch the prefill-SWA kernel on the default FlashMLA path.
62+
assert _run(monkeypatch, decode=decode, family120=family120, kernel=kernel) is False

vllm/envs.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,12 @@
185185
VLLM_DEEPSEEK_V4_INDEXED_D512_SPLIT_PREFILL_WARMUP: bool = True
186186
VLLM_DEEPSEEK_V4_INDEXED_D512_CHUNKED_PREFILL: bool = True
187187
VLLM_DEEPSEEK_V4_FLASHINFER_SM120_DECODE: bool = False
188-
VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL: bool = False
188+
# Default ON: when the SM120 FlashInfer sparse-MLA backend is active (DECODE
189+
# opted in + SM12x + kernel present), the packed prefill runner is used by
190+
# default. Set =0 to force the FlashMLA indexed-D512 (Triton split/merge)
191+
# prefill path. No effect unless that backend is active (see
192+
# is_dsv4_sm120_fi_prefill_active).
193+
VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL: bool = True
189194
VLLM_TRITON_MLA_SPARSE: bool | None = None
190195
VLLM_TRITON_MLA_SPARSE_TOPK_CHUNK_SIZE: int = 512
191196
VLLM_TRITON_MLA_SPARSE_QUERY_CHUNK_SIZE: int = 256
@@ -1485,7 +1490,7 @@ def _resolve_rust_frontend_path() -> str | None:
14851490
int(os.getenv("VLLM_DEEPSEEK_V4_FLASHINFER_SM120_DECODE", "0"))
14861491
),
14871492
"VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL": lambda: bool(
1488-
int(os.getenv("VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL", "0"))
1493+
int(os.getenv("VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL", "1"))
14891494
),
14901495
# Experimental sparse MLA fallback controls.
14911496
# ``VLLM_TRITON_MLA_SPARSE`` unset means auto-select where FlashMLA sparse

vllm/utils/flashinfer.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,30 @@ def has_flashinfer_trtllm_sparse_mla_dsv4() -> bool:
228228
return True
229229

230230

231+
@functools.cache
232+
def is_dsv4_sm120_fi_prefill_active() -> bool:
233+
"""Return ``True`` iff the DeepSeek-V4 FlashInfer SM120 sparse-MLA backend
234+
(which owns the packed-prefill path) is the selected attention backend.
235+
236+
Mirrors the ``_select_dsv4_attn_cls`` gate (deepseek_v4/nvidia/model.py): that
237+
backend is chosen only when SM120 decode is opted in, the device is SM12x, and
238+
the FI SM120 sparse-MLA kernel (PR3395, flashinfer >= 0.6.13) is importable.
239+
The shared ``DeepseekSparseSWAMetadataBuilder`` reads this to gate the
240+
prefill-SWA index kernel that ONLY the packed path consumes -- launching it on
241+
the default FlashMLA/Triton path faults (``cudaErrorLaunchFailure``). So this
242+
must reflect "the packed prefill backend is active", not merely "the kernel is
243+
importable".
244+
"""
245+
import vllm.envs as envs
246+
from vllm.platforms import current_platform
247+
248+
return bool(
249+
envs.VLLM_DEEPSEEK_V4_FLASHINFER_SM120_DECODE
250+
and current_platform.is_device_capability_family(120)
251+
and has_flashinfer_trtllm_sparse_mla_dsv4()
252+
)
253+
254+
231255
def has_flashinfer_sparse_mla_sm120() -> bool:
232256
"""Return ``True`` if FlashInfer sparse MLA decode support is available."""
233257
if not has_flashinfer():

vllm/v1/attention/backends/mla/sparse_swa.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from vllm.model_executor.layers.attention_layer_base import AttentionLayerBase
1111
from vllm.platforms import current_platform
1212
from vllm.triton_utils import tl, triton
13+
from vllm.utils.flashinfer import is_dsv4_sm120_fi_prefill_active
1314
from vllm.utils.math_utils import cdiv
1415
from vllm.v1.attention.backend import (
1516
AttentionBackend,
@@ -427,13 +428,18 @@ def build(
427428
# Prefill SWA indices (paged coords; `token_offset` lets the kernel read at
428429
# absolute prefill positions while writing from index 0) are consumed ONLY by
429430
# the FlashInfer SM120 sparse-MLA fork path. The stock FlashMLA/Triton prefill
430-
# self-computes and never reads them, so gate the launch behind
431-
# VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL (default off). Running it
432-
# unconditionally faulted `_compute_swa_indices_and_lens_kernel` over 32k
433-
# prefill rows (unclamped block_table address arithmetic on masked-off lanes
434-
# -> cudaErrorLaunchFailure under concurrent load).
431+
# self-computes and never reads them, so gate the launch behind both the
432+
# backend-active check AND VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL. This
433+
# builder is SHARED across DSv4 backends, so the env flag alone is not enough
434+
# now that PREFILL defaults on: without the backend-active gate the default
435+
# FlashMLA path would launch this kernel and fault. Running it unconditionally
436+
# faulted `_compute_swa_indices_and_lens_kernel` over 32k prefill rows
437+
# (unclamped block_table address arithmetic on masked-off lanes ->
438+
# cudaErrorLaunchFailure under concurrent load).
435439
want_prefill_swa = (
436-
num_prefill_tokens > 0 and envs.VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL
440+
num_prefill_tokens > 0
441+
and is_dsv4_sm120_fi_prefill_active()
442+
and envs.VLLM_DEEPSEEK_V4_FLASHINFER_SM120_PREFILL
437443
)
438444
if want_prefill_swa:
439445
prefill_swa_indices = self.prefill_swa_indices[:num_prefill_tokens]

0 commit comments

Comments
 (0)