From e2af840c3d0683fb6db59f151a6afef3f3c0ef9e Mon Sep 17 00:00:00 2001 From: Brayden Zhong Date: Tue, 3 Mar 2026 19:46:13 -0500 Subject: [PATCH] Various SM120 improvements (#19721) --- docs/advanced_features/server_arguments.md | 2 +- docs/platforms/ascend_npu_support_features.md | 2 +- docs/references/environment_variables.md | 2 +- .../srt/layers/quantization/fp8_utils.py | 99 ++++++++++++++++--- python/sglang/srt/server_args.py | 2 + 5 files changed, 92 insertions(+), 15 deletions(-) diff --git a/docs/advanced_features/server_arguments.md b/docs/advanced_features/server_arguments.md index e2af93b31..b8d89c208 100644 --- a/docs/advanced_features/server_arguments.md +++ b/docs/advanced_features/server_arguments.md @@ -268,7 +268,7 @@ Please consult the documentation below and [server_args.py](https://github.com/s | `--mm-attention-backend` | Set multimodal attention backend. | `None` | `sdpa`, `fa3`, `fa4`, `triton_attn`, `ascend_attn`, `aiter_attn` | | `--nsa-prefill-backend` | Choose the NSA backend for the prefill stage (overrides `--attention-backend` when running DeepSeek NSA-style attention). | `flashmla_sparse` | `flashmla_sparse`, `flashmla_kv`, `flashmla_auto`, `fa3`, `tilelang`, `aiter`, `trtllm` | | `--nsa-decode-backend` | Choose the NSA backend for the decode stage when running DeepSeek NSA-style attention. Overrides `--attention-backend` for decoding. | `fa3` | `flashmla_sparse`, `flashmla_kv`, `fa3`, `tilelang`, `aiter`, `trtllm` | -| `--fp8-gemm-backend` | Choose the runner backend for Blockwise FP8 GEMM operations. Options: 'auto' (default, auto-selects based on hardware), 'deep_gemm' (JIT-compiled; enabled by default on NVIDIA Hopper (SM90) and Blackwell (SM100) when DeepGEMM is installed), 'flashinfer_trtllm' (optimal for Blackwell and low-latency), 'flashinfer_deepgemm' (Hopper SM90 only; uses swapAB optimization for small M dimensions in decoding), 'cutlass' (optimal for Hopper/Blackwell GPUs and high-throughput), 'triton' (fallback, widely compatible), 'aiter' (ROCm only). **NOTE**: This replaces the deprecated environment variables SGLANG_ENABLE_FLASHINFER_FP8_GEMM and SGLANG_SUPPORT_CUTLASS_BLOCK_FP8. | `auto` | `auto`, `deep_gemm`, `flashinfer_trtllm`, `flashinfer_deepgemm`, `cutlass`, `triton`, `aiter` | +| `--fp8-gemm-backend` | Choose the runner backend for Blockwise FP8 GEMM operations. Options: 'auto' (default, auto-selects based on hardware), 'deep_gemm' (JIT-compiled; enabled by default on NVIDIA Hopper (SM90) and Blackwell (SM100) when DeepGEMM is installed), 'flashinfer_trtllm' (FlashInfer TRTLLM backend; SM100/SM103 only), 'flashinfer_cutlass' (FlashInfer CUTLASS backend, SM120 only), 'flashinfer_deepgemm' (Hopper SM90 only, uses swapAB optimization for small M dimensions in decoding), 'cutlass' (optimal for Hopper/Blackwell GPUs and high-throughput), 'triton' (fallback, widely compatible), 'aiter' (ROCm only). **NOTE**: This replaces the deprecated environment variables SGLANG_ENABLE_FLASHINFER_FP8_GEMM and SGLANG_SUPPORT_CUTLASS_BLOCK_FP8. | `auto` | `auto`, `deep_gemm`, `flashinfer_trtllm`, `flashinfer_cutlass`, `flashinfer_deepgemm`, `cutlass`, `triton`, `aiter` | | `--fp4-gemm-backend` | Choose the runner backend for NVFP4 GEMM operations. Options: 'flashinfer_cutlass' (default), 'auto' (auto-selects between flashinfer_cudnn/flashinfer_cutlass based on CUDA/cuDNN version), 'flashinfer_cudnn' (FlashInfer cuDNN backend, optimal on CUDA 13+ with cuDNN 9.15+), 'flashinfer_trtllm' (FlashInfer TensorRT-LLM backend, requires different weight preparation with shuffling). All backends are from FlashInfer; when FlashInfer is unavailable, sgl-kernel CUTLASS is used as an automatic fallback. **NOTE**: This replaces the deprecated environment variable SGLANG_FLASHINFER_FP4_GEMM_BACKEND. | `flashinfer_cutlass` | `auto`, `flashinfer_cudnn`, `flashinfer_cutlass`, `flashinfer_trtllm` | | `--disable-flashinfer-autotune` | Flashinfer autotune is enabled by default. Set this flag to disable the autotune. | `False` | bool flag (set to enable) | diff --git a/docs/platforms/ascend_npu_support_features.md b/docs/platforms/ascend_npu_support_features.md index 0c1cb379b..1749f5253 100644 --- a/docs/platforms/ascend_npu_support_features.md +++ b/docs/platforms/ascend_npu_support_features.md @@ -194,7 +194,7 @@ click [Server Arguments](https://docs.sglang.io/advanced_features/server_argumen | `--mm-attention-backend` | `None` | `ascend_attn` | A2, A3 | | `--nsa-prefill-backend` | `flashmla_sparse` | `flashmla_sparse`,
`flashmla_decode`,
`fa3`,
`tilelang`,
`aiter` | Special for GPU | | `--nsa-decode-backend` | `fa3` | `flashmla_prefill`,
`flashmla_kv`,
`fa3`,
`tilelang`,
`aiter` | Special for GPU | -| `--fp8-gemm-backend` | `auto` | `auto`,
`deep_gemm`,
`flashinfer_trtllm`,
`cutlass`,
`triton`,
`aiter` | Special for GPU | +| `--fp8-gemm-backend` | `auto` | `auto`,
`deep_gemm`,
`flashinfer_trtllm`,
`flashinfer_cutlass`,
`flashinfer_deepgemm`,
`cutlass`,
`triton`,
`aiter` | Special for GPU | | `--disable-flashinfer-`
`autotune` | `False` | bool flag
(set to enable) | Special for GPU | ## Speculative decoding diff --git a/docs/references/environment_variables.md b/docs/references/environment_variables.md index 7812574ed..f2923765e 100644 --- a/docs/references/environment_variables.md +++ b/docs/references/environment_variables.md @@ -120,7 +120,7 @@ SGLang supports various environment variables that can be used to configure its | `SGLANG_NVFP4_CKPT_FP8_GEMM_IN_ATTN` | Quantize q_b_proj from BF16 to FP8 when launching DeepSeek NVFP4 checkpoint | `false` | | `SGLANG_MOE_NVFP4_DISPATCH` | Use nvfp4 for moe dispatch (on flashinfer_cutlass or flashinfer_cutedsl moe runner backend) | `"false"` | | `SGLANG_NVFP4_CKPT_FP8_NEXTN_MOE` | Quantize moe of nextn layer from BF16 to FP8 when launching DeepSeek NVFP4 checkpoint | `false` | -| `SGLANG_ENABLE_FLASHINFER_FP8_GEMM` (deprecated) | Use flashinfer kernels when running blockwise fp8 GEMM on Blackwell GPUs. **DEPRECATED**: Please use `--fp8-gemm-backend=flashinfer_trtllm` instead. | `false` | +| `SGLANG_ENABLE_FLASHINFER_FP8_GEMM` (deprecated) | Use flashinfer kernels when running blockwise fp8 GEMM on Blackwell GPUs. **DEPRECATED**: Please use `--fp8-gemm-backend=flashinfer_trtllm` (SM100/SM103) or `--fp8-gemm-backend=flashinfer_cutlass` (SM120/SM121 and newer) instead. | `false` | | `SGLANG_SUPPORT_CUTLASS_BLOCK_FP8` (deprecated) | Use Cutlass kernels when running blockwise fp8 GEMM on Hopper or Blackwell GPUs. **DEPRECATED**: Please use `--fp8-gemm-backend=cutlass` instead. | `false` | diff --git a/python/sglang/srt/layers/quantization/fp8_utils.py b/python/sglang/srt/layers/quantization/fp8_utils.py index 66575c69e..ce65b5a01 100644 --- a/python/sglang/srt/layers/quantization/fp8_utils.py +++ b/python/sglang/srt/layers/quantization/fp8_utils.py @@ -144,6 +144,7 @@ class Fp8GemmRunnerBackend(Enum): AUTO = "auto" FLASHINFER_TRTLLM = "flashinfer_trtllm" + FLASHINFER_CUTLASS = "flashinfer_cutlass" FLASHINFER_DEEPGEMM = "flashinfer_deepgemm" CUTLASS = "cutlass" DEEP_GEMM = "deep_gemm" @@ -156,6 +157,9 @@ class Fp8GemmRunnerBackend(Enum): def is_flashinfer_trtllm(self) -> bool: return self == Fp8GemmRunnerBackend.FLASHINFER_TRTLLM + def is_flashinfer_cutlass(self) -> bool: + return self == Fp8GemmRunnerBackend.FLASHINFER_CUTLASS + def is_flashinfer_deepgemm(self) -> bool: return self == Fp8GemmRunnerBackend.FLASHINFER_DEEPGEMM @@ -185,6 +189,20 @@ if is_blackwell_supported() and is_flashinfer_available(): from sglang.srt.utils.custom_op import register_custom_op + @lru_cache(maxsize=1) + def _get_flashinfer_groupwise_backend() -> str: + if get_fp8_gemm_runner_backend().is_flashinfer_cutlass(): + return "cutlass" + if get_fp8_gemm_runner_backend().is_flashinfer_trtllm(): + return "trtllm" + + major, minor = get_device_capability() + # SM120/121: CUTLASS only. + # SM100/103: TRTLLM only. + if major >= 12: + return "cutlass" + return "trtllm" + # Wrap gemm_fp8_nt_groupwise as a custom op so torch.compile does not trace # into flashinfer's JIT compilation code (pathlib/cubin_loader ops). @register_custom_op( @@ -201,13 +219,27 @@ if is_blackwell_supported() and is_flashinfer_available(): weight_scale: torch.Tensor, out_dtype: torch.dtype, ) -> torch.Tensor: + backend = _get_flashinfer_groupwise_backend() + if backend == "cutlass": + # FlashInfer CUTLASS groupwise kernel requires contiguous scale tensors + x_scale = x_scale.contiguous() + weight_scale = weight_scale.contiguous() + return _raw_gemm_fp8_nt_groupwise( + q_input, + weight, + x_scale, + weight_scale, + out_dtype=out_dtype, + backend="cutlass", + scale_major_mode="MN", + ) return _raw_gemm_fp8_nt_groupwise( q_input, weight, x_scale, weight_scale, out_dtype=out_dtype, - backend="trtllm", + backend=backend, ) @@ -237,11 +269,20 @@ def dispatch_w8a8_block_fp8_linear() -> Callable: def _dispatch_explicit_backend(backend: Fp8GemmRunnerBackend) -> Callable: """Dispatch based on explicitly selected backend.""" if backend.is_flashinfer_trtllm(): - if not (is_blackwell_supported() and is_flashinfer_available()): + if not (is_sm100_supported() and is_flashinfer_available()): raise RuntimeError( "FlashInfer FP8 GEMM requested via --fp8-gemm-backend=flashinfer_trtllm, " "but FlashInfer is not available or not supported on this hardware. " - "FlashInfer FP8 GEMM requires Blackwell GPUs and FlashInfer to be installed." + "FlashInfer TRTLLM FP8 GEMM requires SM100/SM103 GPUs and FlashInfer." + ) + return flashinfer_gemm_w8a8_block_fp8_linear_with_fallback + + elif backend.is_flashinfer_cutlass(): + if not (is_blackwell_supported() and is_flashinfer_available()): + raise RuntimeError( + "FlashInfer FP8 GEMM requested via --fp8-gemm-backend=flashinfer_cutlass, " + "but FlashInfer is not available or not supported on this hardware. " + "FlashInfer CUTLASS FP8 GEMM requires Blackwell GPUs and FlashInfer." ) return flashinfer_gemm_w8a8_block_fp8_linear_with_fallback @@ -333,6 +374,10 @@ def initialize_fp8_gemm_config(server_args: ServerArgs) -> None: "SGLANG_SUPPORT_CUTLASS_BLOCK_FP8. Using server argument value." ) + if backend == "auto" and is_sm120_supported(): + # TODO(brayden): Verify if CUTLASS can be set by default once SwapAB is supported + backend = "triton" + FP8_GEMM_RUNNER_BACKEND = Fp8GemmRunnerBackend(backend) @@ -354,24 +399,54 @@ def flashinfer_gemm_w8a8_block_fp8_linear_with_fallback( ) -> torch.Tensor: assert input_scale is None - # FlashInfer TRTLLM backend requires K dimension >= 256 - # Check shape before quantizing, otherwise we run into Flashinfer assertion. - # TODO(brayden): make a better fallback here, maybe to cutlass backend? input_2d = input.view(-1, input.shape[-1]) - k_dim = input_2d.shape[1] # K dimension - - if k_dim < 256: - # Fallback to Triton for shapes that don't meet TRTLLM constraint. + backend = _get_flashinfer_groupwise_backend() + # TRTLLM backend requires K dimension >= 256. + if backend == "trtllm" and input_2d.shape[1] < 256: return triton_w8a8_block_fp8_linear( input, weight, block_size, weight_scale, input_scale, bias ) output_shape = [*input.shape[:-1], weight.shape[0]] + # TRTLLM uses the existing SGLang column-major scale layout. + # CUTLASS with scale_major_mode="MN" expects (k//block_k, m), so we normalize below. q_input, x_scale = sglang_per_token_group_quant_fp8( - input_2d, block_size[1], column_major_scales=True + input_2d, block_size[1], column_major_scales=(backend == "trtllm") ) - # TRTLLM requires column-major scaling factors + if backend == "cutlass": + block_n, block_k = block_size + m, k = input_2d.shape + n = weight.shape[0] + expected_x_scale_shape = (k // block_k, m) + expected_weight_scale_shape = (k // block_k, n // block_n) + if x_scale.shape == (m, k // block_k): + x_scale = x_scale.transpose(-1, -2).contiguous() + if weight_scale.shape == (n // block_n, k // block_k): + weight_scale = weight_scale.transpose(-1, -2).contiguous() + assert x_scale.shape == expected_x_scale_shape, ( + "FlashInfer CUTLASS groupwise FP8 expects A scale layout " + f"(k//block_k, m) for scale_major_mode='MN', got {tuple(x_scale.shape)}; " + f"expected {expected_x_scale_shape}. " + f"strides={x_scale.stride()} is_contiguous={x_scale.is_contiguous()} " + f"m={m} n={n} k={k} block_size={block_size}" + ) + assert weight_scale.shape == expected_weight_scale_shape, ( + "FlashInfer CUTLASS groupwise FP8 expects B scale layout " + f"(k//block_k, n//block_n) for scale_major_mode='MN', got {tuple(weight_scale.shape)}; " + f"expected {expected_weight_scale_shape}. " + f"strides={weight_scale.stride()} is_contiguous={weight_scale.is_contiguous()} " + f"m={m} n={n} k={k} block_size={block_size}" + ) + assert x_scale.dtype == torch.float32, ( + "FlashInfer CUTLASS groupwise FP8 expects x_scale dtype float32, " + f"got {x_scale.dtype}." + ) + assert weight_scale.dtype == torch.float32, ( + "FlashInfer CUTLASS groupwise FP8 expects weight_scale dtype float32, " + f"got {weight_scale.dtype}." + ) + # TRTLLM path continues using the original quantized scale layout. output = gemm_fp8_nt_groupwise( q_input, weight, diff --git a/python/sglang/srt/server_args.py b/python/sglang/srt/server_args.py index 0b8c1b280..b8504f36f 100644 --- a/python/sglang/srt/server_args.py +++ b/python/sglang/srt/server_args.py @@ -198,6 +198,7 @@ FP8_GEMM_RUNNER_BACKEND_CHOICES = [ "auto", "deep_gemm", "flashinfer_trtllm", + "flashinfer_cutlass", "flashinfer_deepgemm", "cutlass", "triton", @@ -4085,6 +4086,7 @@ class ServerArgs: "Options: 'auto' (default, auto-selects based on hardware), " "'deep_gemm' (JIT-compiled; enabled by default on NVIDIA Hopper (SM90) and Blackwell (SM100) when DeepGEMM is installed), " "'flashinfer_trtllm' (optimal for Blackwell and low-latency), " + "'flashinfer_cutlass' (FlashInfer CUTLASS groupwise FP8 GEMM), " "'flashinfer_deepgemm' (Hopper SM90 only; uses swapAB optimization for small M dimensions in decoding), " "'cutlass' (optimal for Hopper/Blackwell GPUs and high-throughput), " "'triton' (fallback, widely compatible), "