Files
wehub-resource-sync 94057c3d3e
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:38:16 +08:00

475 lines
16 KiB
Python

from __future__ import annotations
import logging
from typing import TYPE_CHECKING
import torch
import triton
import triton.language as tl
from torch.nn import Module
from torch.nn.parameter import Parameter
from sglang.srt.distributed import get_tp_group
from sglang.srt.distributed.device_communicators.pynccl_allocator import (
use_symmetric_memory,
)
from sglang.srt.layers.dp_attention import is_allocation_symmetric
from sglang.srt.layers.moe.utils import RoutingMethodType
from sglang.srt.runtime_context import get_server_args
from sglang.srt.utils import (
is_flashinfer_available,
log_info_on_rank0,
set_weight_attrs,
)
from sglang.srt.utils.common import is_sm100_supported, next_power_of_2
_MXFP8_QUANTIZE_BACKEND = "cute-dsl" if is_sm100_supported() else "cuda"
if is_flashinfer_available():
from flashinfer import mxfp8_quantize, shuffle_matrix_a, shuffle_matrix_sf_a
from flashinfer.fp4_quantization import block_scale_interleave
from flashinfer.fused_moe import trtllm_fp4_block_scale_routed_moe
from flashinfer.fused_moe.core import (
_maybe_get_cached_w3_w1_permute_indices,
get_w2_permute_indices_with_cache,
)
logger = logging.getLogger(__name__)
if TYPE_CHECKING:
from sglang.srt.layers.moe.token_dispatcher import CombineInput, DispatchOutput
from sglang.srt.utils.common import get_bool_env_var
_USE_OFFICIAL_SHUFFLE = get_bool_env_var(
"SGLANG_MXFP4_USE_OFFICIAL_SHUFFLE", default="true"
)
class PackTopkIds:
@classmethod
def execute(
cls, topk_ids: torch.Tensor, topk_weights: torch.Tensor
) -> torch.Tensor:
return cls.triton(topk_ids, topk_weights)
@classmethod
def vanilla(
cls, topk_ids: torch.Tensor, topk_weights: torch.Tensor
) -> torch.Tensor:
weight_bits = (
topk_weights.to(torch.bfloat16).view(torch.int16).to(torch.int32) & 0xFFFF
)
return (topk_ids.to(torch.int32) << 16) | weight_bits
@classmethod
def triton(cls, topk_ids: torch.Tensor, topk_weights: torch.Tensor) -> torch.Tensor:
assert (
topk_ids.shape == topk_weights.shape
), f"shape mismatch: {topk_ids.shape=} vs {topk_weights.shape=}"
assert topk_ids.ndim >= 1, f"expected >=1D, got {topk_ids.shape=}"
assert (
topk_ids.dtype == torch.int32
), f"topk_ids must be int32, got {topk_ids.dtype}"
assert (
topk_weights.dtype == torch.float32
), f"topk_weights must be float32, got {topk_weights.dtype}"
assert topk_ids.is_contiguous(), "topk_ids must be contiguous"
assert topk_weights.is_contiguous(), "topk_weights must be contiguous"
out = torch.empty_like(topk_ids, dtype=torch.int32)
numel = out.numel()
if numel == 0:
return out
BLOCK_SIZE = 1024
grid = (triton.cdiv(numel, BLOCK_SIZE),)
_pack_topk_ids_triton_kernel[grid](
topk_ids,
topk_weights,
out,
numel,
BLOCK_SIZE=BLOCK_SIZE,
)
return out
@triton.jit
def _pack_topk_ids_triton_kernel(
topk_ids_ptr,
topk_weights_ptr,
out_ptr,
numel,
BLOCK_SIZE: tl.constexpr,
):
pid = tl.program_id(0)
offsets = pid * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
mask = offsets < numel
ids = tl.load(topk_ids_ptr + offsets, mask=mask, other=0)
w = tl.load(topk_weights_ptr + offsets, mask=mask, other=0.0)
w_bf16 = w.to(tl.bfloat16)
w_i16 = w_bf16.to(tl.int16, bitcast=True)
w_i32 = w_i16.to(tl.int32) & 0xFFFF
ids_i32 = ids.to(tl.int32)
packed = (ids_i32 << 16) | w_i32
tl.store(out_ptr + offsets, packed, mask=mask)
class Mxfp4FlashinferTrtllmMoEMethod:
def __init__(self, fp8_method, prefix: str):
self._fp8 = fp8_method
self.prefix = prefix
self.flashinfer_mxfp4_moe_precision = (
get_server_args().flashinfer_mxfp4_moe_precision
)
def create_moe_runner(self, layer, moe_runner_config):
self.moe_runner_config = moe_runner_config
swiglu_limit = moe_runner_config.swiglu_limit
assert (
swiglu_limit is not None
), f"swiglu_limit must be non-None for DeepSeek V4 (got {swiglu_limit!r})"
self._gemm1_clamp_limit_tensor = (
torch.full(
(layer.num_local_experts,),
swiglu_limit,
dtype=torch.float32,
device=layer.w13_weight.device,
)
if swiglu_limit is not None
else None
)
def create_weights(
self,
layer,
num_experts: int,
hidden_size: int,
intermediate_size_per_partition: int,
params_dtype,
**extra_weight_attrs,
):
from sglang.srt.layers.moe.fused_moe_triton import FusedMoeWeightScaleSupported
fp4_block_k = 32
w13_weight = Parameter(
torch.empty(
num_experts,
2 * intermediate_size_per_partition,
hidden_size // 2,
dtype=torch.int8,
),
requires_grad=False,
)
w2_weight = Parameter(
torch.empty(
num_experts,
hidden_size,
intermediate_size_per_partition // 2,
dtype=torch.int8,
),
requires_grad=False,
)
layer.register_parameter("w13_weight", w13_weight)
set_weight_attrs(w13_weight, extra_weight_attrs)
layer.register_parameter("w2_weight", w2_weight)
set_weight_attrs(w2_weight, extra_weight_attrs)
w13_weight_scale = Parameter(
torch.ones(
num_experts,
2 * intermediate_size_per_partition,
hidden_size // fp4_block_k,
dtype=torch.float32,
),
requires_grad=False,
)
w2_weight_scale = Parameter(
torch.ones(
num_experts,
hidden_size,
intermediate_size_per_partition // fp4_block_k,
dtype=torch.float32,
),
requires_grad=False,
)
w13_weight_scale.format_ue8m0 = False
w2_weight_scale.format_ue8m0 = False
scale_attrs = dict(extra_weight_attrs)
scale_attrs["quant_method"] = FusedMoeWeightScaleSupported.BLOCK.value
layer.register_parameter("w13_weight_scale_inv", w13_weight_scale)
set_weight_attrs(w13_weight_scale, scale_attrs)
layer.register_parameter("w2_weight_scale_inv", w2_weight_scale)
set_weight_attrs(w2_weight_scale, scale_attrs)
def process_weights_after_loading(self, layer: Module) -> None:
from sglang.srt.layers.quantization.utils import reorder_w1w3_to_w3w1
self._fp8.process_weights_after_loading(layer)
if getattr(layer, "_mega_moe_weights_built", False):
return
w13_w, w13_s = reorder_w1w3_to_w3w1(
layer.w13_weight.data, layer.w13_weight_scale_inv.data
)
layer.w13_weight = Parameter(w13_w, requires_grad=False)
layer.w13_weight_scale_inv = Parameter(w13_s, requires_grad=False)
log_info_on_rank0(
logger,
f"Shuffling FP4 expert weights for TRT-LLM MxFP4 kernel "
f"(layer: {self.prefix})...",
)
w13 = layer.w13_weight.data
w2 = layer.w2_weight.data
w13_scale = layer.w13_weight_scale_inv.data
w2_scale = layer.w2_weight_scale_inv.data
num_experts = w13.shape[0]
if w13_scale.dtype == torch.float32:
w13_scale = w13_scale.to(torch.float8_e8m0fnu)
w2_scale = w2_scale.to(torch.float8_e8m0fnu)
epilogue_tile_m = 128
g1_w, g1_s, g2_w, g2_s = [], [], [], []
if _USE_OFFICIAL_SHUFFLE:
cache: dict = {}
for i in range(num_experts):
w13_u8 = w13[i].view(torch.uint8)
w13_s_u8 = w13_scale[i].view(torch.uint8)
w2_u8 = w2[i].view(torch.uint8)
w2_s_u8 = w2_scale[i].view(torch.uint8)
perm = _maybe_get_cached_w3_w1_permute_indices(
cache,
w13_u8,
epilogue_tile_m,
)
g1_w.append(w13_u8[perm.to(w13_u8.device)].contiguous())
perm_sf = _maybe_get_cached_w3_w1_permute_indices(
cache,
w13_s_u8,
epilogue_tile_m,
num_elts_per_sf=16,
)
g1_s.append(
block_scale_interleave(
w13_s_u8[perm_sf.to(w13_s_u8.device)].contiguous()
)
)
perm = get_w2_permute_indices_with_cache(
cache,
w2_u8,
epilogue_tile_m,
)
g2_w.append(w2_u8[perm.to(w2_u8.device)].contiguous())
perm_sf = get_w2_permute_indices_with_cache(
cache,
w2_s_u8,
epilogue_tile_m,
num_elts_per_sf=16,
)
g2_s.append(
block_scale_interleave(
w2_s_u8[perm_sf.to(w2_s_u8.device)].contiguous()
)
)
else:
for i in range(num_experts):
g1_w.append(shuffle_matrix_a(w13[i].view(torch.uint8), epilogue_tile_m))
g1_s.append(
shuffle_matrix_sf_a(w13_scale[i].view(torch.uint8), epilogue_tile_m)
)
g2_w.append(shuffle_matrix_a(w2[i].view(torch.uint8), epilogue_tile_m))
g2_s.append(
shuffle_matrix_sf_a(w2_scale[i].view(torch.uint8), epilogue_tile_m)
)
layer.w13_weight = Parameter(torch.stack(g1_w), requires_grad=False)
layer.w13_weight_scale_inv = Parameter(
torch.stack(g1_s)
.view(torch.float8_e4m3fn)
.reshape(num_experts, w13.shape[1], -1),
requires_grad=False,
)
layer.w2_weight = Parameter(torch.stack(g2_w), requires_grad=False)
layer.w2_weight_scale_inv = Parameter(
torch.stack(g2_s)
.view(torch.float8_e4m3fn)
.reshape(num_experts, w2.shape[1], -1),
requires_grad=False,
)
self._register_static_scale_ones(layer)
torch.cuda.empty_cache()
def _register_static_scale_ones(self, layer: Module) -> None:
device = layer.w13_weight.device
for name in (
"output1_scale_scalar",
"output1_scale_gate_scalar",
"output2_scale_scalar",
):
layer.register_buffer(
name,
torch.ones(layer.num_local_experts, device=device, dtype=torch.float32),
persistent=False,
)
def apply(
self,
layer: Module,
dispatch_output: DispatchOutput,
) -> CombineInput:
from sglang.srt.layers.moe.token_dispatcher import StandardCombineInput
from sglang.srt.layers.moe.topk import TopKOutputChecker
hidden_states = dispatch_output.hidden_states
topk_output = dispatch_output.topk_output
w13 = layer.w13_weight
w2 = layer.w2_weight
w13_scale = layer.w13_weight_scale_inv
w2_scale = layer.w2_weight_scale_inv
intermediate_size = w2.shape[2] * 2 if w2.dtype == torch.uint8 else w2.shape[2]
hidden_size = w13.shape[2] * 2 if w13.dtype == torch.uint8 else w13.shape[2]
num_local_experts = layer.num_local_experts
if w13_scale.dim() == 2:
w13_scale = w13_scale.reshape(num_local_experts, 2 * intermediate_size, -1)
if w2_scale.dim() == 2:
w2_scale = w2_scale.reshape(num_local_experts, hidden_size, -1)
if TopKOutputChecker.format_is_standard(topk_output):
topk_ids = topk_output.topk_ids
topk_weights = topk_output.topk_weights
elif TopKOutputChecker.format_is_bypassed(topk_output):
raise NotImplementedError(
"the old code in this branch is WRONG. e.g. it does not consider HashTopK, and may miss args"
)
else:
raise ValueError(f"Unsupported topk output format: {topk_output.format}")
packed_topk = PackTopkIds.execute(topk_ids, topk_weights)
precision = self.flashinfer_mxfp4_moe_precision
if precision == "bf16":
assert hidden_states.dtype == torch.bfloat16
x_quant = hidden_states
x_scale = None
origin_dim = x_quant.shape[-1]
if hidden_size != origin_dim:
x_quant = torch.nn.functional.pad(
x_quant,
(0, hidden_size - origin_dim),
mode="constant",
value=0.0,
)
elif precision == "default":
x_quant, x_scale = mxfp8_quantize(
hidden_states,
False,
alignment=hidden_size,
backend=_MXFP8_QUANTIZE_BACKEND,
)
x_scale = x_scale.view(torch.float8_e4m3fn).reshape(
*hidden_states.shape[:-1], -1
)
else:
raise NotImplementedError(f"Unsupported mxfp4 moe precision: {precision}")
with use_symmetric_memory(
get_tp_group(), disabled=not is_allocation_symmetric()
):
num_tokens = x_quant.shape[0]
out_hidden_size = (
x_quant.shape[-1] * 2
if x_quant.dtype == torch.uint8
else x_quant.shape[-1]
)
symm_output = torch.empty(
num_tokens, out_hidden_size, dtype=torch.bfloat16, device=x_quant.device
)
output = trtllm_fp4_block_scale_routed_moe(
topk_ids=packed_topk,
routing_bias=None,
hidden_states=x_quant,
hidden_states_scale=x_scale,
gemm1_weights=w13,
gemm1_weights_scale=w13_scale,
gemm1_bias=None,
gemm1_alpha=None,
gemm1_beta=None,
gemm1_clamp_limit=self._gemm1_clamp_limit_tensor,
gemm2_weights=w2,
gemm2_weights_scale=w2_scale,
gemm2_bias=None,
output1_scale_scalar=layer.output1_scale_scalar,
output1_scale_gate_scalar=layer.output1_scale_gate_scalar,
output2_scale_scalar=layer.output2_scale_scalar,
num_experts=layer.num_experts,
top_k=packed_topk.shape[1],
n_group=1,
topk_group=1,
intermediate_size=intermediate_size,
local_expert_offset=layer.moe_ep_rank * layer.num_local_experts,
local_num_experts=num_local_experts,
routed_scaling_factor=1.0,
routing_method_type=int(RoutingMethodType.TopK),
do_finalize=True,
tune_max_num_tokens=next_power_of_2(x_quant.shape[0]),
output=symm_output,
)[0]
return StandardCombineInput(hidden_states=output)
def maybe_fuse_routed_scale_and_shared_add(
experts,
routed: torch.Tensor,
shared: torch.Tensor | None,
routed_scaling_factor: float,
) -> torch.Tensor:
# When MxFP4 fusion is on, the upstream `routed *= scale` is skipped and
# the scaling is folded into the shared-add via `shared.add_(routed,
# alpha=scale)`. With no shared output, the missing scale is applied
# in-place. Otherwise `routed` is already scale-final and we just add
# `shared` (or pass through if there is none).
from sglang.srt.layers.quantization.mxfp4_flashinfer_cutlass_moe import (
Mxfp4FlashinferCutlassMoEMethod,
)
from sglang.srt.layers.quantization.mxfp4_marlin_moe import (
Mxfp4MarlinMoEMethod,
)
fused = isinstance(
experts.quant_method,
(
Mxfp4FlashinferTrtllmMoEMethod,
Mxfp4FlashinferCutlassMoEMethod,
Mxfp4MarlinMoEMethod,
),
)
if fused:
if shared is not None:
return shared.add_(routed, alpha=routed_scaling_factor)
return routed.mul_(routed_scaling_factor)
if shared is not None:
routed += shared
return routed