Support Cutedsl BF16 GEMM JIT kernel (#30117)

Co-authored-by: Brayden Zhong <brayden@radixark.ai>
Co-authored-by: Baizhou Zhang <sobereddiezhang@gmail.com>
This commit is contained in:
Brayden Zhong
2026-07-06 19:16:16 -07:00
committed by GitHub
co-authored by Brayden Zhong Baizhou Zhang
parent 267ff1b5f9
commit e85ef54877
6 changed files with 1506 additions and 0 deletions
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,7 @@
from __future__ import annotations
import logging
from enum import Enum
from typing import TYPE_CHECKING, List, Optional
logger = logging.getLogger(__name__)
@@ -46,6 +47,7 @@ if TYPE_CHECKING:
DispatchOutput,
StandardDispatchOutput,
)
from sglang.srt.server_args import ServerArgs
_is_cpu_amx_available = cpu_has_amx_support()
@@ -62,6 +64,53 @@ if _is_npu:
from sglang.srt.hardware_backend.npu.utils import npu_format_cast
class Bf16GemmBackend(Enum):
AUTO = "auto"
CUTEDSL = "cutedsl"
def is_auto(self) -> bool:
return self == Bf16GemmBackend.AUTO
def is_cutedsl(self) -> bool:
return self == Bf16GemmBackend.CUTEDSL
_BF16_GEMM_BACKEND: Optional[Bf16GemmBackend] = None
_cutedsl_bf16_gemm = None
_use_cutedsl_bf16_gemm = None
def initialize_bf16_gemm_config(server_args: ServerArgs) -> None:
global _BF16_GEMM_BACKEND, _cutedsl_bf16_gemm, _use_cutedsl_bf16_gemm
backend = Bf16GemmBackend(server_args.bf16_gemm_backend)
if backend.is_cutedsl():
from sglang.srt.utils import is_sm100_supported
if not is_sm100_supported():
raise ValueError(
"--bf16-gemm-backend cutedsl requires SM100/SM103 (Blackwell)"
)
from sglang.jit_kernel.cutedsl_bf16_gemm import (
cutedsl_bf16_gemm,
use_cutedsl_bf16_gemm,
)
_cutedsl_bf16_gemm = cutedsl_bf16_gemm
_use_cutedsl_bf16_gemm = use_cutedsl_bf16_gemm
_BF16_GEMM_BACKEND = backend
def get_bf16_gemm_backend() -> Bf16GemmBackend:
global _BF16_GEMM_BACKEND
if _BF16_GEMM_BACKEND is None:
_BF16_GEMM_BACKEND = Bf16GemmBackend.AUTO
return _BF16_GEMM_BACKEND
class UnquantizedEmbeddingMethod(QuantizeMethodBase):
"""Unquantized method for embeddings."""
@@ -152,6 +201,22 @@ class UnquantizedLinearMethod(LinearMethodBase):
elif _use_aiter and type(layer.weight.data) is torch.Tensor:
return tgemm.mm(x, layer.weight, bias, otype=x.dtype)
elif (
get_bf16_gemm_backend().is_cutedsl()
and x.is_cuda
and x.dtype == torch.bfloat16
and layer.weight.dtype == torch.bfloat16
and (bias is None or bias.dtype == torch.bfloat16)
and _use_cutedsl_bf16_gemm(
x.numel() // x.shape[-1],
layer.weight.shape[0],
layer.weight.shape[1],
)
):
x_shapes = x.shape
output = _cutedsl_bf16_gemm(x.view(-1, x_shapes[-1]), layer.weight, bias)
return output.view(*x_shapes[:-1], -1)
return F.linear(x, layer.weight, bias)
+2
View File
@@ -80,6 +80,7 @@ from sglang.srt.layers.dp_attention import (
from sglang.srt.layers.moe import initialize_moe_config
from sglang.srt.layers.quantization.fp4_utils import initialize_fp4_gemm_config
from sglang.srt.layers.quantization.fp8_utils import initialize_fp8_gemm_config
from sglang.srt.layers.quantization.unquant import initialize_bf16_gemm_config
from sglang.srt.lora.lora_drainer import LoRADrainer
from sglang.srt.lora.lora_overlap_loader import LoRAOverlapLoader
from sglang.srt.managers.hisparse_coordinator import HiSparseCoordinator
@@ -757,6 +758,7 @@ class Scheduler(
# Initialize GEMM-related configuration for FP8 and FP4 backends.
initialize_fp8_gemm_config(self.server_args)
initialize_fp4_gemm_config(self.server_args)
initialize_bf16_gemm_config(self.server_args)
# This must be called after initialize_moe_config
self.require_mlp_sync = require_mlp_sync(self.server_args)
+12
View File
@@ -115,6 +115,7 @@ from sglang.srt.layers.quantization.fp8_kernel import (
from sglang.srt.layers.quantization.mxfp4_flashinfer_trtllm_moe import (
maybe_fuse_routed_scale_and_shared_add,
)
from sglang.srt.layers.quantization.unquant import get_bf16_gemm_backend
from sglang.srt.layers.radix_attention import RadixAttention
from sglang.srt.layers.rotary_embedding import get_rope_wrapper
from sglang.srt.layers.utils import PPMissingLayer
@@ -1975,12 +1976,23 @@ class DeepseekV2AttentionMLA(
# When the module is wrapped with LoRA, the fused GEMM fast-path would
# bypass the adapter because it reads weight.T directly.
lora_active = getattr(self.fused_qkv_a_proj_with_mqa, "set_lora", False)
cutedsl_backend = get_bf16_gemm_backend().is_cutedsl()
if cutedsl_backend:
from sglang.jit_kernel.cutedsl_bf16_gemm import use_cutedsl_bf16_gemm
if (
(not isinstance(hidden_states, tuple))
and hidden_states.shape[0] >= 1
and hidden_states.shape[0] <= 16
and self.use_min_latency_fused_a_gemm
and not lora_active
and not (
cutedsl_backend
and use_cutedsl_bf16_gemm(
hidden_states.shape[0],
self.fused_qkv_a_proj_with_mqa.weight.shape[0],
self.fused_qkv_a_proj_with_mqa.weight.shape[1],
)
)
):
qkv_latent = dsv3_fused_a_gemm(
hidden_states,
+10
View File
@@ -265,6 +265,8 @@ FP4_GEMM_RUNNER_BACKEND_CHOICES = [
"marlin",
]
BF16_GEMM_BACKEND_CHOICES = ["auto", "cutedsl"]
RADIX_EVICTION_POLICY_CHOICES = ["lru", "lfu", "slru", "priority"]
RL_ON_POLICY_TARGET_CHOICES = ["fsdp"]
@@ -1346,6 +1348,14 @@ class ServerArgs:
choices=FP4_GEMM_RUNNER_BACKEND_CHOICES,
),
] = "auto"
bf16_gemm_backend: A[
str,
Arg(
help="Choose the backend for unquantized BF16 GEMM operations. Options: 'auto' (default; uses cuBLAS via torch.nn.functional.linear), 'cutedsl' (SGLang JIT CuTe DSL TGV BF16 GEMM on SM10X; dispatches between the CuTe DSL kernel and cuBLAS).",
cli_name="--bf16-gemm-backend",
choices=BF16_GEMM_BACKEND_CHOICES,
),
] = "auto"
dsa_prefill_backend: A[
Optional[str],
Arg(