Add FP8 Blockwise GEMM Backend Flag --fp8-gemm-backend (#14379)
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
from typing import Callable, List, Optional, Tuple
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from enum import Enum
|
||||
from typing import TYPE_CHECKING, Callable, List, Optional, Tuple
|
||||
|
||||
import torch
|
||||
|
||||
@@ -6,7 +10,9 @@ from sglang.srt.environ import envs
|
||||
from sglang.srt.layers import deep_gemm_wrapper
|
||||
from sglang.srt.layers.quantization.fp8_kernel import sglang_per_token_group_quant_fp8
|
||||
from sglang.srt.layers.quantization.mxfp4_tensor import MXFP4QuantizeUtil
|
||||
from sglang.srt.utils import ceil_div, is_blackwell_supported, offloader
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from sglang.srt.server_args import ServerArgs
|
||||
|
||||
try:
|
||||
from vllm import _custom_ops as ops
|
||||
@@ -29,14 +35,20 @@ from sglang.srt.layers.quantization.fp8_kernel import (
|
||||
)
|
||||
from sglang.srt.utils import (
|
||||
ceil_align,
|
||||
ceil_div,
|
||||
get_bool_env_var,
|
||||
get_cuda_version,
|
||||
get_device_capability,
|
||||
is_blackwell_supported,
|
||||
is_cuda,
|
||||
is_flashinfer_available,
|
||||
is_hip,
|
||||
is_sm90_supported,
|
||||
offloader,
|
||||
)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_is_hip = is_hip()
|
||||
_is_cuda = is_cuda()
|
||||
_is_fp8_fnuz = is_fp8_fnuz()
|
||||
@@ -125,42 +137,166 @@ def normalize_e4m3fn_to_e4m3fnuz(
|
||||
return weight, weight_scale, input_scale
|
||||
|
||||
|
||||
# TODO(ch-wan): define these backends in --moe-runner-backend
|
||||
def cutlass_block_fp8_supported() -> bool:
|
||||
if not get_bool_env_var("SGLANG_SUPPORT_CUTLASS_BLOCK_FP8"):
|
||||
return False
|
||||
if _is_cuda:
|
||||
major, minor = torch.cuda.get_device_capability()
|
||||
sm_version = major * 10 + minor
|
||||
cuda_version = tuple(map(int, torch.version.cuda.split(".")))
|
||||
if cuda_version >= (12, 0) and sm_version >= 90:
|
||||
return True
|
||||
return False
|
||||
class Fp8GemmRunnerBackend(Enum):
|
||||
"""Enum for FP8 GEMM runner backend selection."""
|
||||
|
||||
AUTO = "auto"
|
||||
FLASHINFER = "flashinfer_trtllm"
|
||||
CUTLASS = "cutlass"
|
||||
DEEP_GEMM = "deep_gemm"
|
||||
TRITON = "triton"
|
||||
AITER = "aiter"
|
||||
|
||||
def is_auto(self) -> bool:
|
||||
return self == Fp8GemmRunnerBackend.AUTO
|
||||
|
||||
def is_flashinfer(self) -> bool:
|
||||
return self == Fp8GemmRunnerBackend.FLASHINFER
|
||||
|
||||
def is_cutlass(self) -> bool:
|
||||
return self == Fp8GemmRunnerBackend.CUTLASS
|
||||
|
||||
def is_deep_gemm(self) -> bool:
|
||||
return self == Fp8GemmRunnerBackend.DEEP_GEMM
|
||||
|
||||
def is_triton(self) -> bool:
|
||||
return self == Fp8GemmRunnerBackend.TRITON
|
||||
|
||||
def is_aiter(self) -> bool:
|
||||
return self == Fp8GemmRunnerBackend.AITER
|
||||
|
||||
|
||||
CUTLASS_BLOCK_FP8_SUPPORTED = cutlass_block_fp8_supported()
|
||||
ENABLE_FLASHINFER_FP8_GEMM = (
|
||||
envs.SGLANG_ENABLE_FLASHINFER_FP8_GEMM.get()
|
||||
and is_blackwell_supported()
|
||||
and is_flashinfer_available()
|
||||
)
|
||||
if ENABLE_FLASHINFER_FP8_GEMM:
|
||||
FP8_GEMM_RUNNER_BACKEND: Fp8GemmRunnerBackend | None = None
|
||||
|
||||
|
||||
def _check_cutlass_block_fp8_hardware_support() -> bool:
|
||||
"""Return True if CUTLASS block FP8 is supported (Hopper or newer with CUDA 12.0+)."""
|
||||
return is_sm90_supported() or is_blackwell_supported()
|
||||
|
||||
|
||||
if is_blackwell_supported() and is_flashinfer_available():
|
||||
from flashinfer.gemm import gemm_fp8_nt_groupwise
|
||||
|
||||
|
||||
def dispatch_w8a8_block_fp8_linear() -> Callable:
|
||||
if ENABLE_FLASHINFER_FP8_GEMM:
|
||||
"""
|
||||
Dispatch to the appropriate FP8 block linear implementation.
|
||||
|
||||
This function selects the backend based on:
|
||||
1. The --fp8-gemm-backend server argument (preferred)
|
||||
2. Auto-detection based on hardware capabilities
|
||||
"""
|
||||
backend = get_fp8_gemm_runner_backend()
|
||||
|
||||
# Handle explicit backend selection via --fp8-gemm-backend
|
||||
if not backend.is_auto():
|
||||
return _dispatch_explicit_backend(backend)
|
||||
|
||||
# Auto mode: Select based purely on hardware/backend availability
|
||||
return _dispatch_auto_backend()
|
||||
|
||||
|
||||
def _dispatch_explicit_backend(backend: Fp8GemmRunnerBackend) -> Callable:
|
||||
"""Dispatch based on explicitly selected backend."""
|
||||
if backend.is_flashinfer():
|
||||
if not (is_blackwell_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."
|
||||
)
|
||||
return flashinfer_gemm_w8a8_block_fp8_linear
|
||||
elif CUTLASS_BLOCK_FP8_SUPPORTED:
|
||||
|
||||
elif backend.is_cutlass():
|
||||
if not _check_cutlass_block_fp8_hardware_support():
|
||||
raise RuntimeError(
|
||||
"CUTLASS block FP8 requested via --fp8-gemm-backend=cutlass, "
|
||||
"but hardware does not support it. CUTLASS block FP8 requires "
|
||||
"Hopper (SM90+) GPUs with CUDA 12.0+."
|
||||
)
|
||||
return cutlass_w8a8_block_fp8_linear_with_fallback
|
||||
|
||||
elif backend.is_aiter():
|
||||
if not _use_aiter:
|
||||
raise RuntimeError(
|
||||
"AITER backend requested via --fp8-gemm-backend=aiter, "
|
||||
"but AITER is not available. AITER requires AMD GPUs with "
|
||||
"SGLANG_USE_AITER=1 environment variable set."
|
||||
)
|
||||
return aiter_w8a8_block_fp8_linear
|
||||
|
||||
elif backend.is_deep_gemm():
|
||||
if not deep_gemm_wrapper.ENABLE_JIT_DEEPGEMM:
|
||||
raise RuntimeError(
|
||||
"DeepGEMM backend requested via --fp8-gemm-backend=deep_gemm, "
|
||||
"but DeepGEMM is not available. This usually means the deep_gemm package "
|
||||
"is not installed or has been disabled via SGLANG_ENABLE_JIT_DEEPGEMM=0."
|
||||
)
|
||||
return deepgemm_w8a8_block_fp8_linear_with_fallback
|
||||
|
||||
elif backend.is_triton():
|
||||
return triton_w8a8_block_fp8_linear
|
||||
|
||||
else:
|
||||
raise ValueError(f"Unknown FP8 GEMM backend: {backend}")
|
||||
|
||||
|
||||
def _dispatch_auto_backend() -> Callable:
|
||||
"""Auto-select the best backend based on hardware capabilities."""
|
||||
# Priority order for auto selection:
|
||||
# 1. DeepGEMM (if enabled and available)
|
||||
# 2. FlashInfer TRTLLM (if Blackwell GPU and FlashInfer available)
|
||||
# 3. CUTLASS (if Hopper+ GPU and CUDA 12.0+)
|
||||
# 4. AITER (if AMD GPU with AITER enabled)
|
||||
# 5. Triton (fallback)
|
||||
|
||||
if deep_gemm_wrapper.ENABLE_JIT_DEEPGEMM:
|
||||
return deepgemm_w8a8_block_fp8_linear_with_fallback
|
||||
elif is_blackwell_supported() and is_flashinfer_available():
|
||||
return flashinfer_gemm_w8a8_block_fp8_linear
|
||||
elif _check_cutlass_block_fp8_hardware_support():
|
||||
return cutlass_w8a8_block_fp8_linear_with_fallback
|
||||
elif _use_aiter:
|
||||
return aiter_w8a8_block_fp8_linear
|
||||
elif deep_gemm_wrapper.ENABLE_JIT_DEEPGEMM:
|
||||
return deepgemm_w8a8_block_fp8_linear_with_fallback
|
||||
else:
|
||||
return triton_w8a8_block_fp8_linear
|
||||
|
||||
|
||||
def initialize_fp8_gemm_config(server_args: ServerArgs) -> None:
|
||||
"""Initialize FP8 GEMM configuration."""
|
||||
global FP8_GEMM_RUNNER_BACKEND
|
||||
|
||||
backend = server_args.fp8_gemm_runner_backend
|
||||
|
||||
# TODO(brayden): Remove env-based overrides in v0.5.7, they will be fully removed in v0.5.7.
|
||||
# Only check environment variables when the server args is not set, server args should take priority.
|
||||
if backend == "auto":
|
||||
if envs.SGLANG_ENABLE_FLASHINFER_FP8_GEMM.get():
|
||||
backend = "flashinfer_trtllm"
|
||||
elif envs.SGLANG_SUPPORT_CUTLASS_BLOCK_FP8.get():
|
||||
backend = "cutlass"
|
||||
else:
|
||||
if (
|
||||
envs.SGLANG_ENABLE_FLASHINFER_FP8_GEMM.get()
|
||||
or envs.SGLANG_SUPPORT_CUTLASS_BLOCK_FP8.get()
|
||||
):
|
||||
logger.warning(
|
||||
f"FP8 GEMM backend set to '{backend}' via --fp8-gemm-backend overrides "
|
||||
"environment variables SGLANG_ENABLE_FLASHINFER_FP8_GEMM and "
|
||||
"SGLANG_SUPPORT_CUTLASS_BLOCK_FP8. Using server argument value."
|
||||
)
|
||||
|
||||
FP8_GEMM_RUNNER_BACKEND = Fp8GemmRunnerBackend(backend)
|
||||
|
||||
|
||||
def get_fp8_gemm_runner_backend() -> Fp8GemmRunnerBackend:
|
||||
"""Get the current FP8 GEMM runner backend."""
|
||||
global FP8_GEMM_RUNNER_BACKEND
|
||||
if FP8_GEMM_RUNNER_BACKEND is None:
|
||||
FP8_GEMM_RUNNER_BACKEND = Fp8GemmRunnerBackend.AUTO
|
||||
return FP8_GEMM_RUNNER_BACKEND
|
||||
|
||||
|
||||
def flashinfer_gemm_w8a8_block_fp8_linear(
|
||||
input: torch.Tensor,
|
||||
weight: torch.Tensor,
|
||||
|
||||
Reference in New Issue
Block a user