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 5, 19:45 UTC
[NVIDIA] Bugfix NVFP4 DGX Spark and RTX50 (#38423)
Signed-off-by: johnnynunez <johnnynuca14@gmail.com> Signed-off-by: Johnny <johnnynuca14@gmail.com>
Johnny committed
Mar 30, 2026 at 18:36 UTC
b4a2f3ac369043b4a734160215575f2bc8037678
15 files changed
+86
-20
CMakeLists.txt
+1
-1
@@ -309,7 +309,7 @@ if(VLLM_GPU_LANG STREQUAL "CUDA")
309
SET(CUTLASS_ENABLE_HEADERS_ONLY ON CACHE BOOL "Enable only the header library")
310
311
# Set CUTLASS_REVISION. Used for FetchContent. Also fixes some bogus messages when building.
312
- set(CUTLASS_REVISION "v4.2.1")
312
+ set(CUTLASS_REVISION "v4.4.2")
313
314
# Use the specified CUTLASS source directory for compilation if VLLM_CUTLASS_SRC_DIR is provided
315
if (DEFINED ENV{VLLM_CUTLASS_SRC_DIR})
csrc/quantization/fp4/nvfp4_quant_entry.cu
+29
@@ -16,6 +16,7 @@
16
17
#include <torch/all.h>
18
19
+#include "cutlass_extensions/common.hpp"
20
#include "nvfp4_utils.cuh"
21
22
#if (defined(ENABLE_NVFP4_SM100) && ENABLE_NVFP4_SM100) || \
@@ -53,12 +54,27 @@ void silu_and_mul_scaled_fp4_experts_quant_sm1xxa(
54
torch::Tensor const& output_scale_offset_by_experts);
55
#endif
56
57
+static bool nvfp4_quant_sm_supported() {
58
+ const int32_t sm = get_sm_version_num();
59
+#if defined(ENABLE_NVFP4_SM100) && ENABLE_NVFP4_SM100
60
+ if (sm >= 100 && sm < 120) return true;
61
+#endif
62
+#if defined(ENABLE_NVFP4_SM120) && ENABLE_NVFP4_SM120
63
+ if (sm >= 120 && sm < 130) return true;
64
+#endif
65
+ return false;
66
+}
67
+
68
void scaled_fp4_quant_out(torch::Tensor const& input,
69
torch::Tensor const& input_sf,
70
bool is_sf_swizzled_layout, torch::Tensor& output,
71
torch::Tensor& output_sf) {
72
#if (defined(ENABLE_NVFP4_SM100) && ENABLE_NVFP4_SM100) || \
73
(defined(ENABLE_NVFP4_SM120) && ENABLE_NVFP4_SM120)
74
+ TORCH_CHECK(nvfp4_quant_sm_supported(),
75
+ "No compiled nvfp4 quantization kernel for SM ",
76
+ get_sm_version_num(),
77
+ ". Recompile with the appropriate CUDA arch.");
78
return scaled_fp4_quant_sm1xxa(output, input, output_sf, input_sf,
79
is_sf_swizzled_layout);
80
#endif
@@ -100,6 +116,10 @@ void scaled_fp4_experts_quant(
116
torch::Tensor const& output_scale_offset_by_experts) {
117
#if (defined(ENABLE_NVFP4_SM100) && ENABLE_NVFP4_SM100) || \
118
(defined(ENABLE_NVFP4_SM120) && ENABLE_NVFP4_SM120)
119
+ TORCH_CHECK(nvfp4_quant_sm_supported(),
120
+ "No compiled nvfp4 experts quantization kernel for SM ",
121
+ get_sm_version_num(),
122
+ ". Recompile with the appropriate CUDA arch.");
123
return scaled_fp4_experts_quant_sm1xxa(
124
output, output_scale, input, input_global_scale, input_offset_by_experts,
125
output_scale_offset_by_experts);
@@ -112,6 +132,10 @@ void silu_and_mul_nvfp4_quant(torch::Tensor& output, torch::Tensor& output_sf,
132
torch::Tensor& input, torch::Tensor& input_sf) {
133
#if (defined(ENABLE_NVFP4_SM100) && ENABLE_NVFP4_SM100) || \
134
(defined(ENABLE_NVFP4_SM120) && ENABLE_NVFP4_SM120)
135
+ TORCH_CHECK(nvfp4_quant_sm_supported(),
136
+ "No compiled silu_and_mul nvfp4 quantization kernel for SM ",
137
+ get_sm_version_num(),
138
+ ". Recompile with the appropriate CUDA arch.");
139
return silu_and_mul_nvfp4_quant_sm1xxa(output, output_sf, input, input_sf);
140
#endif
141
TORCH_CHECK_NOT_IMPLEMENTED(
@@ -125,6 +149,11 @@ void silu_and_mul_scaled_fp4_experts_quant(
149
torch::Tensor const& output_scale_offset_by_experts) {
150
#if (defined(ENABLE_NVFP4_SM100) && ENABLE_NVFP4_SM100) || \
151
(defined(ENABLE_NVFP4_SM120) && ENABLE_NVFP4_SM120)
152
+ TORCH_CHECK(nvfp4_quant_sm_supported(),
153
+ "No compiled silu_and_mul nvfp4 experts quantization kernel "
154
+ "for SM ",
155
+ get_sm_version_num(),
156
+ ". Recompile with the appropriate CUDA arch.");
157
return silu_and_mul_scaled_fp4_experts_quant_sm1xxa(
158
output, output_scale, input, input_global_scale, input_offset_by_experts,
159
output_scale_offset_by_experts);
csrc/quantization/fp4/nvfp4_scaled_mm_entry.cu
+13
-1
@@ -63,5 +63,17 @@ void cutlass_scaled_fp4_mm(torch::Tensor& D, const torch::Tensor& A,
63
bool cutlass_scaled_mm_supports_fp4(int64_t cuda_device_capability) {
64
int runtimeVersion;
65
cudaRuntimeGetVersion(&runtimeVersion);
66
- return cuda_device_capability >= 100 && runtimeVersion >= 12080;
66
+ if (runtimeVersion < 12080) return false;
67
+ // Only report support when the SM-specific kernel was actually compiled in,
68
+ // so the Python-side backend selector does not choose CUTLASS and then hit
69
+ // TORCH_CHECK_NOT_IMPLEMENTED (or worse, fall through to Marlin).
70
+#if defined(ENABLE_NVFP4_SM100) && ENABLE_NVFP4_SM100
71
+ if (cuda_device_capability >= 100 && cuda_device_capability < 120)
72
+ return true;
73
+#endif
74
+#if defined(ENABLE_NVFP4_SM120) && ENABLE_NVFP4_SM120
75
+ if (cuda_device_capability >= 120 && cuda_device_capability < 130)
76
+ return true;
77
+#endif
78
+ return false;
79
}
csrc/quantization/machete/machete_mainloop.cuh
+1
@@ -154,6 +154,7 @@ struct MacheteCollectiveMma {
154
struct DispatchPolicy {
155
constexpr static int Stages = PipelineStages;
156
using ClusterShape = ClusterShape_MNK;
157
+ using ArchTag = arch::Sm90;
158
using Schedule = KernelScheduleType;
159
};
160
docker/Dockerfile
+4
-1
@@ -590,7 +590,10 @@ RUN --mount=type=cache,target=/root/.cache/uv \
590
# Install FlashInfer JIT cache (requires CUDA-version-specific index URL)
591
# https://docs.flashinfer.ai/installation.html
592
# From versions.json: .flashinfer.version
593
-ARG FLASHINFER_VERSION=0.6.6
593
+# 0.6.7: CUTLASS 4.4.2 bump, fixes TMA grouped GEMM on SM12x (flashinfer#2798)
594
+# TODO: bump to 0.6.8 when released for NVFP4/MXFP4 group GEMMs on
595
+# SM120/SM121 (RTX 50 / DGX Spark) via flashinfer#2738
596
+ARG FLASHINFER_VERSION=0.6.7
597
RUN --mount=type=cache,target=/root/.cache/uv \
598
uv pip install --system flashinfer-jit-cache==${FLASHINFER_VERSION} \
599
--extra-index-url https://flashinfer.ai/whl/cu$(echo $CUDA_VERSION | cut -d. -f1,2 | tr -d '.') \
docker/Dockerfile.nightly_torch
+5
-2
@@ -217,13 +217,16 @@ RUN pip install setuptools==75.6.0 packaging==23.2 ninja==1.11.1.3 build==1.2.2.
217
218
219
# build flashinfer for torch nightly from source around 10 mins
220
-# release version: v0.6.6
220
+# release version: v0.6.7
221
+# 0.6.7: CUTLASS 4.4.2 bump, fixes TMA grouped GEMM on SM12x (flashinfer#2798)
222
+# TODO: bump to 0.6.8 when released for NVFP4/MXFP4 group GEMMs on
223
+# SM120/SM121 (RTX 50 / DGX Spark) via flashinfer#2738
224
# todo(elainewy): cache flashinfer build result for faster build
225
ENV CCACHE_DIR=/root/.cache/ccache
226
RUN --mount=type=cache,target=/root/.cache/ccache \
227
--mount=type=cache,target=/root/.cache/uv \
228
echo "git clone flashinfer..." \
226
- && git clone --depth 1 --branch v0.6.6 --recursive https://github.com/flashinfer-ai/flashinfer.git \
229
+ && git clone --depth 1 --branch v0.6.7 --recursive https://github.com/flashinfer-ai/flashinfer.git \
230
&& cd flashinfer \
231
&& git submodule update --init --recursive \
232
&& echo "finish git clone flashinfer..." \
docker/versions.json
+1
-1
@@ -68,7 +68,7 @@
68
"default": "true"
69
},
70
"FLASHINFER_VERSION": {
71
- "default": "0.6.6"
71
+ "default": "0.6.7"
72
},
73
"GDRCOPY_CUDA_VERSION": {
74
"default": "12.8"
requirements/cuda.txt
+2
-2
@@ -9,8 +9,8 @@ torchaudio==2.10.0
9
# These must be updated alongside torch
10
torchvision==0.25.0 # Required for phi3v processor. See https://github.com/pytorch/vision?tab=readme-ov-file#installation for corresponding version
11
# FlashInfer should be updated together with the Dockerfile
12
-flashinfer-python==0.6.6
13
-flashinfer-cubin==0.6.6
12
+flashinfer-python==0.6.7
13
+flashinfer-cubin==0.6.7
14
# Cap nvidia-cudnn-frontend (transitive dep of flashinfer) due to
15
# breaking changes in 1.19.0
16
nvidia-cudnn-frontend>=1.13.0,<1.19.0
tests/kernels/moe/test_unquantized_backend_selection.py
-4
@@ -57,7 +57,6 @@ def test_select_default_backend_by_platform(
57
moe_config = make_dummy_moe_config()
58
selected_backend = select_unquantized_moe_backend(
59
moe_config=moe_config,
60
- use_ep=False,
60
use_dp=False,
61
)
62
@@ -90,7 +89,6 @@ def test_select_rocm_aiter_backend(mock_aiter_enabled, mock_has_flashinfer):
89
moe_config = make_dummy_moe_config()
90
selected_backend = select_unquantized_moe_backend(
91
moe_config=moe_config,
93
- use_ep=False,
92
use_dp=False,
93
)
94
@@ -129,7 +127,6 @@ def test_select_cuda_flashinfer_trtllm_backend(
127
128
selected_backend = select_unquantized_moe_backend(
129
moe_config=moe_config,
132
- use_ep=True,
130
use_dp=False,
131
)
132
@@ -171,7 +168,6 @@ def test_select_cuda_flashinfer_cutlass_backend(
168
169
selected_backend = select_unquantized_moe_backend(
170
moe_config=moe_config,
174
- use_ep=True, # CUTLASS requires EP
171
use_dp=False, # CUTLASS doesn't support DP
172
)
173
vllm/model_executor/layers/fused_moe/experts/trtllm_fp8_moe.py
+5
@@ -406,6 +406,11 @@ class TrtLlmFp8ExpertsMonolithic(TrtLlmFp8ExpertsBase, mk.FusedMoEExpertsMonolit
406
if self.routing_method_type == RoutingMethodType.DeepSeekV3:
407
router_logits = router_logits.to(torch.float32)
408
409
+ # Currently FI requires bfloat16 routing bias.
410
+ # https://github.com/flashinfer-ai/flashinfer/issues/2909
411
+ if e_score_correction_bias is not None:
412
+ e_score_correction_bias = e_score_correction_bias.to(torch.bfloat16)
413
+
414
out = flashinfer.fused_moe.trtllm_fp8_per_tensor_scale_moe(
415
routing_logits=router_logits,
416
routing_bias=e_score_correction_bias,
vllm/model_executor/layers/fused_moe/experts/trtllm_nvfp4_moe.py
+8
@@ -5,6 +5,7 @@ import flashinfer
5
import torch
6
7
import vllm.model_executor.layers.fused_moe.modular_kernel as mk
8
+from vllm.logger import init_logger
9
from vllm.model_executor.layers.fused_moe.activation import MoEActivation
10
from vllm.model_executor.layers.fused_moe.config import (
11
FusedMoEConfig,
@@ -27,6 +28,8 @@ from vllm.model_executor.layers.quantization.utils.quant_utils import (
28
from vllm.platforms import current_platform
29
from vllm.utils.flashinfer import has_flashinfer_trtllm_fused_moe
30
31
+logger = init_logger(__name__)
32
+
33
34
class TrtLlmNvFp4ExpertsBase:
35
"""
@@ -315,6 +318,11 @@ class TrtLlmNvFp4ExpertsMonolithic(
318
else router_logits
319
)
320
321
+ # Currently FI requires bfloat16 routing bias.
322
+ # https://github.com/flashinfer-ai/flashinfer/issues/2909
323
+ if e_score_correction_bias is not None:
324
+ e_score_correction_bias = e_score_correction_bias.to(torch.bfloat16)
325
+
326
# Invoke kernel.
327
return flashinfer.fused_moe.trtllm_fp4_block_scale_moe(
328
routing_logits=router_logits,
vllm/model_executor/layers/fused_moe/flashinfer_cutlass_moe.py
+1
-1
@@ -361,7 +361,7 @@ class FlashInferExperts(mk.FusedMoEExpertsModular):
361
fc1_expert_weights = w1
362
fc2_expert_weights = w2
363
else:
364
- quant_scales = None
364
+ quant_scales = []
365
a1q_scale = None
366
fc1_expert_weights = w1
367
fc2_expert_weights = w2
vllm/model_executor/layers/fused_moe/oracle/unquantized.py
+2
-4
@@ -70,7 +70,6 @@ def map_unquantized_backend(runner_backend: MoEBackend) -> UnquantizedMoeBackend
70
71
def select_unquantized_moe_backend(
72
moe_config: FusedMoEConfig,
73
- use_ep: bool,
73
use_dp: bool,
74
) -> UnquantizedMoeBackend:
75
"""
@@ -96,7 +95,6 @@ def select_unquantized_moe_backend(
95
# FlashInfer CUTLASS MoE is only supported on Hopper and later GPUS
96
flashinfer_cutlass_available = (
97
has_flashinfer_cutlass_fused_moe()
99
- and use_ep
98
and (not use_dp)
99
and current_platform.has_device_capability(90)
100
)
@@ -161,9 +159,9 @@ def select_unquantized_moe_backend(
159
"to enable it for better performance.",
160
scope="local",
161
)
164
- elif use_ep and (not use_dp):
162
+ elif not use_dp and flashinfer_cutlass_available:
163
logger.info_once(
166
- "FlashInfer MoE is available for EP"
164
+ "FlashInfer CUTLASS MoE is available"
165
" but not enabled, consider setting"
166
" VLLM_USE_FLASHINFER_MOE_FP16=1 to enable it.",
167
scope="local",
vllm/model_executor/layers/fused_moe/unquantized_fused_moe_method.py
-1
@@ -61,7 +61,6 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, CustomOp):
61
super().__init__(moe)
62
self.unquantized_backend = select_unquantized_moe_backend(
63
moe_config=self.moe,
64
- use_ep=self.moe.moe_parallel_config.use_ep,
64
use_dp=self.moe.moe_parallel_config.dp_size > 1,
65
)
66
vllm/model_executor/layers/quantization/utils/nvfp4_utils.py
+14
-2
@@ -55,8 +55,16 @@ def select_nvfp4_linear_backend() -> NvFp4LinearBackend:
55
elif envs.VLLM_USE_NVFP4_CT_EMULATIONS:
56
backend = NvFp4LinearBackend.EMULATION
57
elif envs.VLLM_NVFP4_GEMM_BACKEND is None:
58
- # Auto-select best available backend
59
- if current_platform.has_device_capability(100) and has_flashinfer():
58
+ # Auto-select best available backend.
59
+ # cutlass_fp4_supported() checks that the vLLM NVFP4 kernels (both
60
+ # quantization and GEMM) were compiled for the current SM version.
61
+ # FlashInfer backends still rely on the vLLM quantization kernels,
62
+ # so we gate them on the same check.
63
+ if (
64
+ cutlass_fp4_supported()
65
+ and current_platform.has_device_capability(100)
66
+ and has_flashinfer()
67
+ ):
68
backend = NvFp4LinearBackend.FLASHINFER_CUTLASS
69
elif cutlass_fp4_supported():
70
backend = NvFp4LinearBackend.VLLM_CUTLASS
@@ -72,6 +80,10 @@ def select_nvfp4_linear_backend() -> NvFp4LinearBackend:
80
NvFp4LinearBackend.FLASHINFER_CUDNN,
81
):
82
assert has_flashinfer(), f"FlashInfer is required for {backend}"
83
+ assert cutlass_fp4_supported(), (
84
+ f"{backend} requires vLLM NVFP4 quantization kernels compiled "
85
+ f"for the current GPU (SM {current_platform.get_device_capability()})"
86
+ )
87
elif backend == NvFp4LinearBackend.VLLM_CUTLASS:
88
assert cutlass_fp4_supported(), f"Cutlass is required for {backend}"
89
elif backend == NvFp4LinearBackend.MARLIN: