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
475 lines
16 KiB
Python
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
|