[MoE] Unify DeepEPMoE+MoriEPMoE through AITER MoeRunner pre/post-permute (#23760)

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Cheng Wan
2026-05-17 02:25:52 -07:00
committed by GitHub
co-authored by Claude Opus 4.7
parent 568ba7216a
commit be3c425788
13 changed files with 398 additions and 330 deletions
@@ -1054,6 +1054,10 @@ class MaybeTboDeepEPDispatcher(BaseDispatcher):
NixlEPDispatcher(**kwargs) for _ in range(num_inner_dispatchers)
]
@property
def expert_mask_gpu(self):
return self._inners[0].expert_mask_gpu
def _execute(self, name, tbo_subbatch_index: Optional[int] = None, **kwargs):
return getattr(self._inners[tbo_subbatch_index or 0], name)(**kwargs)
+11 -285
View File
@@ -19,15 +19,10 @@ from sglang.srt.layers.moe.fused_moe_triton.layer import (
FusedMoE,
moe_forward_piecewise_cuda_graph_impl,
)
from sglang.srt.layers.moe.rocm_moe_utils import upscale, upscale_mxfp4
from sglang.srt.layers.moe.token_dispatcher.deepep import (
DeepEPLLCombineInput,
DeepEPNormalCombineInput,
)
from sglang.srt.layers.moe.token_dispatcher.moriep import (
MoriEPLLCombineInput,
MoriEPNormalCombineInput,
)
from sglang.srt.layers.moe.topk import TopKOutput, TopKOutputChecker
from sglang.srt.layers.quantization.base_config import QuantizationConfig
from sglang.srt.layers.quantization.compressed_tensors.compressed_tensors import (
@@ -36,11 +31,10 @@ from sglang.srt.layers.quantization.compressed_tensors.compressed_tensors import
from sglang.srt.layers.quantization.compressed_tensors.schemes import (
NPUCompressedTensorsW4A16Int4DynamicMoE,
)
from sglang.srt.layers.quantization.fp8 import Fp8Config, Fp8MoEMethod
from sglang.srt.layers.quantization.fp8 import Fp8Config
from sglang.srt.layers.quantization.fp8_kernel import is_fp8_fnuz
from sglang.srt.layers.quantization.quark.schemes import QuarkW4A4MXFp4MoE
from sglang.srt.layers.quantization.w4afp8 import W4AFp8Config, W4AFp8MoEMethod
from sglang.srt.utils import get_bool_env_var, get_int_env_var, is_hip, is_npu
from sglang.srt.utils import get_bool_env_var, is_hip, is_npu
if TYPE_CHECKING:
from sglang.srt.layers.moe.token_dispatcher import (
@@ -54,20 +48,13 @@ _is_npu = is_npu()
_is_fp8_fnuz = is_fp8_fnuz()
_use_aiter = get_bool_env_var("SGLANG_USE_AITER") and _is_hip
if _use_aiter:
from aiter import ActivationType, QuantType
from aiter.fused_moe import fused_moe
elif _is_npu:
if _is_npu:
import torch_npu
logger = logging.getLogger(__name__)
if _is_npu:
import torch_npu
class DeepEPMoE(FusedMoE):
"""
MoE Expert Parallel Impl based on DeepEP (https://github.com/deepseek-ai/DeepEP/tree/main)
@@ -105,7 +92,9 @@ class DeepEPMoE(FusedMoE):
routed_scaling_factor=routed_scaling_factor,
**kwargs,
)
if _use_aiter or _is_npu:
if _use_aiter:
self.deprecate_flag = True
elif _is_npu:
self.deprecate_flag = False
elif deep_gemm_wrapper.ENABLE_JIT_DEEPGEMM and isinstance(
quant_config, Fp8Config
@@ -158,19 +147,6 @@ class DeepEPMoE(FusedMoE):
assert (
deep_gemm_wrapper.ENABLE_JIT_DEEPGEMM
), f"DeepEP {self.deepep_mode} mode requires deep_gemm"
if _use_aiter:
# expert_mask is of size (self.num_local_experts + 1),
# the extra 1 is for invalid rank_id (in original deepep, the invalid rank_id is -1, but aiter does not allow -1, we use a mask to make those ids invalid)
# for instance, if we have 4 experts on this rank, we would have a expert_mask like:
# self.expert_mask = [1, 1, 1, 1, 0]
# idx from 0-3 is valid and will be processed, while idx == 4 will be masked out
self.expert_mask = torch.zeros(
(self.num_local_experts + 1),
device=torch.cuda.current_device(),
dtype=torch.int,
)
# the last one is invalid rank_id
self.expert_mask[:-1] = 1
def forward(
self,
@@ -203,16 +179,11 @@ class DeepEPMoE(FusedMoE):
topk_output,
)
# TODO: can we call super().forward here?
dispatch_output = self.dispatcher.dispatch(
hidden_states=hidden_states, topk_output=topk_output
)
combine_input = self.run_moe_core(dispatch_output)
hidden_states = self.dispatcher.combine(
combine_input=combine_input,
)
return hidden_states
return self.dispatcher.combine(combine_input=combine_input)
def dispatch(
self,
@@ -230,17 +201,11 @@ class DeepEPMoE(FusedMoE):
):
if self.deprecate_flag:
return super().run_moe_core(
dispatch_output,
)
return super().run_moe_core(dispatch_output)
from sglang.srt.layers.moe.token_dispatcher import DispatchOutputChecker
if _use_aiter:
assert DispatchOutputChecker.format_is_deepep(dispatch_output)
# in forward_aiter, we skip token permutation and unpermutation, which have been fused inside aiter kernel
output = self.forward_aiter(dispatch_output)
elif _is_npu:
if _is_npu:
assert DispatchOutputChecker.format_is_deepep(dispatch_output)
output = self.forward_npu(dispatch_output)
elif DispatchOutputChecker.format_is_deepep_normal(dispatch_output):
@@ -292,42 +257,6 @@ class DeepEPMoE(FusedMoE):
overlap_args=overlap_args,
)
def forward_aiter(
self,
dispatch_output: Union[DeepEPNormalDispatchOutput, DeepEPLLDispatchOutput],
):
hidden_states, topk_ids, topk_weights = (
dispatch_output.hidden_states,
dispatch_output.topk_ids,
dispatch_output.topk_weights,
)
if hidden_states.shape[0] == 0:
return hidden_states
# in original deepep, idx == -1 meaning invalid and will not be processed.
# aiter does not accept -1, we use a expert mask to make these idx invalid
# (idx == num_local_experts) meaning not used in aiter fused_moe
topk_ids_copy = topk_ids.to(torch.int32)
topk_ids_copy[topk_ids_copy == -1] = self.num_local_experts
return fused_moe(
hidden_states,
self.w13_weight,
self.w2_weight,
topk_weights,
topk_ids_copy,
w1_scale=self.w13_weight_scale_inv,
w2_scale=self.w2_weight_scale_inv,
quant_type=QuantType.per_128x128,
activation=(
ActivationType.Silu
if self.moe_runner_config.activation == "silu"
else ActivationType.Gelu
),
expert_mask=self.expert_mask,
)
def forward_unquantized_deepep_ll(
self,
dispatch_output: DeepEPLLDispatchOutput,
@@ -644,214 +573,11 @@ class NpuFuseEPMoE(DeepEPMoE):
)
class MoriEPMoE(DeepEPMoE):
def __init__(
self,
num_experts: int,
top_k: int,
hidden_size: int,
intermediate_size: int,
layer_id: int,
num_fused_shared_experts: int = 0,
params_dtype: Optional[torch.dtype] = None,
quant_config: Optional[QuantizationConfig] = None,
prefix: str = "",
activation: str = "silu",
routed_scaling_factor: Optional[float] = None,
**kwargs,
):
super().__init__(
num_experts=num_experts,
top_k=top_k,
hidden_size=hidden_size,
intermediate_size=intermediate_size,
layer_id=layer_id,
num_fused_shared_experts=num_fused_shared_experts,
params_dtype=params_dtype,
quant_config=quant_config,
prefix=prefix,
activation=activation,
routed_scaling_factor=routed_scaling_factor,
**kwargs,
)
assert _use_aiter, "Mori need to be used together with aiter as of now"
self.expert_mask = torch.zeros(
(self.num_experts),
device=torch.cuda.current_device(),
dtype=torch.int32,
)
expert_start_idx = self.moe_ep_rank * self.num_local_experts
expert_end_idx = expert_start_idx + self.num_local_experts
self.expert_mask[expert_start_idx:expert_end_idx] = 1
self.mori_moe_max_input_tokens = get_int_env_var(
"SGLANG_MORI_MOE_MAX_INPUT_TOKENS", 0
)
def forward(
self,
hidden_states: torch.Tensor,
topk_output: TopKOutput,
):
num_token = hidden_states.shape[0]
dispatch_output = self.dispatcher.dispatch(
hidden_states=hidden_states, topk_output=topk_output
)
combine_input = self.run_moe_core(dispatch_output)
hidden_states = self.dispatcher.combine(
combine_input=combine_input,
)
return hidden_states[:num_token]
def run_moe_core(
self,
dispatch_output: DispatchOutput,
):
scale = None
is_fp8_quant = isinstance(self.quant_method, Fp8MoEMethod)
is_quark_w4a4 = hasattr(self, "scheme") and isinstance(
self.scheme, QuarkW4A4MXFp4MoE
)
(
dispatch_a1,
dispatch_scale,
dispatch_ids,
dispatch_weights,
dispatch_recv_token_num,
origin_topk_ids,
origin_topk_weights,
output_dtype,
) = (
dispatch_output.hidden_states,
dispatch_output.hidden_states_scale,
dispatch_output.topk_ids,
dispatch_output.topk_weights,
dispatch_output.num_recv_tokens_per_expert,
dispatch_output.origin_topk_ids,
dispatch_output.origin_topk_weights,
dispatch_output.out_dtype,
)
# Truncate dispatch tensors to reduce MoE computation on padding rows.
# dispatch_a1 has shape (M, hidden_size) where M is the full buffer size,
# but only the first dispatch_recv_token_num rows are valid.
# mori combine only reads [0, totalRecvTokenNum), so the truncated
# output can be passed directly without padding back.
if self.mori_moe_max_input_tokens > 0:
limit = self.mori_moe_max_input_tokens
dispatch_a1 = dispatch_a1[:limit]
if dispatch_scale is not None:
dispatch_scale = dispatch_scale[:limit]
dispatch_ids = dispatch_ids[:limit]
dispatch_weights = dispatch_weights[:limit]
w13_weight = self.w13_weight
w2_weight = self.w2_weight
w13_scale = None
w2_scale = None
quant_type = QuantType.No
if (
not is_fp8_quant
and dispatch_scale is not None
and dispatch_a1.dtype != torch.float4_e2m1fn_x2
):
if is_quark_w4a4:
# W4A4 model with FP8 dispatch: must dequant FP8->BF16 first,
# because the FP4 per_1x32 quantization path needs BF16 input
dispatch_a1 = upscale(
dispatch_a1, dispatch_scale, dispatch_recv_token_num, output_dtype
)
dispatch_scale = None
else:
# Non-W4A4 model with FP8 dispatch: pass FP8 hidden_states + scale
# directly to fused_moe, avoiding unnecessary dequant->requant round-trip
quant_type = QuantType.per_128x128
if dispatch_a1.dtype == torch.float4_e2m1fn_x2 and dispatch_scale is not None:
if is_fp8_quant:
# FP8 weights + FP4 dispatch is not supported by fused_moe kernels
# (no kernel for q_dtype_a=fp4x2, q_dtype_w=fp8).
# Must dequant FP4->BF16 first; fused_moe will re-quant to FP8 internally.
dispatch_a1 = upscale_mxfp4(
dispatch_a1, dispatch_scale, dispatch_recv_token_num, output_dtype
)
dispatch_scale = None
elif quant_type == QuantType.No:
# Skip upscale_mxfp4: pass FP4 hidden_states + scale directly to fused_moe
# fused_moe with QuantType.per_1x32 can accept pre-quantized fp4x2 input
quant_type = QuantType.per_1x32
if is_quark_w4a4:
if hasattr(torch, "float4_e2m1fn_x2"):
w13_weight = self.w13_weight.view(torch.float4_e2m1fn_x2)
w2_weight = self.w2_weight.view(torch.float4_e2m1fn_x2)
w13_scale = self.w13_weight_scale
w2_scale = self.w2_weight_scale
quant_type = QuantType.per_1x32
if hasattr(self.w13_weight, "is_shuffled"):
w13_weight.is_shuffled = True
w2_weight.is_shuffled = True
elif is_fp8_quant:
if hasattr(self, "w13_weight_scale_inv"):
w13_scale = self.w13_weight_scale_inv
if hasattr(self, "w2_weight_scale_inv"):
w2_scale = self.w2_weight_scale_inv
# Only set per_128x128 if quant_type was not already set by
# a prior dispatch path (e.g. FP4 dispatch sets per_1x32)
if quant_type == QuantType.No:
quant_type = QuantType.per_128x128
# [KK TODO] should to call the apply of quant method to handle fused moe
hidden_states = fused_moe(
hidden_states=dispatch_a1,
w1=w13_weight,
w2=w2_weight,
w1_scale=w13_scale,
w2_scale=w2_scale,
a1_scale=dispatch_scale,
topk_weight=dispatch_weights,
topk_ids=dispatch_ids,
quant_type=quant_type,
activation=(
ActivationType.Silu
if self.moe_runner_config.activation == "silu"
else ActivationType.Gelu
),
expert_mask=self.expert_mask,
num_local_tokens=dispatch_recv_token_num,
dtype=output_dtype,
)
from sglang.srt.layers.moe.token_dispatcher import DispatchOutputChecker
combine_input_wrapper = (
MoriEPNormalCombineInput
if DispatchOutputChecker.format_is_deepep_normal(dispatch_output)
else MoriEPLLCombineInput
)
return combine_input_wrapper(
hidden_states=hidden_states,
topk_ids=dispatch_output.origin_topk_ids,
topk_weights=dispatch_output.origin_topk_weights,
)
def get_moe_impl_class(quant_config: Optional[QuantizationConfig]):
# [TODO] kk, temporary solution
if get_moe_a2a_backend().is_mori():
return MoriEPMoE
if (
get_moe_a2a_backend().is_deepep()
get_moe_a2a_backend().is_mori()
or get_moe_a2a_backend().is_deepep()
or get_moe_a2a_backend().is_mooncake()
or get_moe_a2a_backend().is_nixl()
):
+332 -30
View File
@@ -2,17 +2,32 @@ from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
from typing import TYPE_CHECKING, Optional
from typing import TYPE_CHECKING, Any, Optional, Union
import torch
from sglang.srt.layers.moe.moe_runner.base import (
MoeQuantInfo,
MoeRunnerConfig,
register_fused_func,
MoeRunnerCore,
RunnerInput,
RunnerOutput,
register_post_permute,
register_pre_permute,
)
from sglang.srt.layers.moe.utils import MoeRunnerBackend
from sglang.srt.utils import get_int_env_var
if TYPE_CHECKING:
from sglang.srt.layers.moe.token_dispatcher.base import CombineInput
from sglang.srt.layers.moe.token_dispatcher.deepep import (
DeepEPLLDispatchOutput,
DeepEPNormalDispatchOutput,
)
from sglang.srt.layers.moe.token_dispatcher.moriep import (
MoriEPLLDispatchOutput,
MoriEPNormalDispatchOutput,
)
from sglang.srt.layers.moe.token_dispatcher.standard import (
StandardCombineInput,
StandardDispatchOutput,
@@ -43,22 +58,116 @@ class AiterMoeQuantInfo(MoeQuantInfo):
intermediate_pad: int = 0
@dataclass
class AiterRunnerInput(RunnerInput):
hidden_states: torch.Tensor
topk_ids: torch.Tensor # int32
topk_weights: torch.Tensor # float32
# Effective activation quant_type (may differ from quant_info.quant_type
# after the dispatch-aware decision in mori pre_permute).
quant_type: AiterQuantType
# Per-token activation scale produced by an EP dispatcher (mori). Falls
# back to quant_info.a13_scale when None.
a1_scale: Optional[torch.Tensor] = None
# Mori-only fused_moe kwargs.
num_local_tokens: Optional[torch.Tensor] = None
output_dtype: Optional[torch.dtype] = None
@property
def runner_backend(self) -> MoeRunnerBackend:
return MoeRunnerBackend.AITER
@dataclass
class AiterRunnerOutput(RunnerOutput):
hidden_states: torch.Tensor
@property
def runner_backend(self) -> MoeRunnerBackend:
return MoeRunnerBackend.AITER
_AITER_ACTIVATIONS = {"silu": "Silu", "swiglu": "Swiglu"}
@register_fused_func("none", "aiter")
def fused_experts_none_to_aiter(
def _aiter_activation(activation: str):
from aiter import ActivationType
return getattr(ActivationType, _AITER_ACTIVATIONS.get(activation, "Gelu"))
def _aiter_quant_type(quant_type: AiterQuantType):
from aiter import QuantType
return getattr(QuantType, quant_type.value)
class AiterRunnerCore(MoeRunnerCore):
def run(
self,
runner_input: AiterRunnerInput,
quant_info: AiterMoeQuantInfo,
running_state: dict,
hooks: Optional[Any] = None,
) -> AiterRunnerOutput:
assert not self.config.no_combine, "no_combine=True is not supported by AITER"
if runner_input.hidden_states.shape[0] == 0:
return AiterRunnerOutput(hidden_states=runner_input.hidden_states)
from aiter.fused_moe import fused_moe
a1_scale = (
runner_input.a1_scale
if runner_input.a1_scale is not None
else quant_info.a13_scale
)
extra: dict = {}
if runner_input.num_local_tokens is not None:
extra["num_local_tokens"] = runner_input.num_local_tokens
if runner_input.output_dtype is not None:
extra["dtype"] = runner_input.output_dtype
output = fused_moe(
hidden_states=runner_input.hidden_states,
w1=quant_info.w13_weight,
w2=quant_info.w2_weight,
topk_weight=runner_input.topk_weights,
topk_ids=runner_input.topk_ids,
quant_type=_aiter_quant_type(runner_input.quant_type),
activation=_aiter_activation(self.config.activation),
w1_scale=quant_info.w13_scale,
w2_scale=quant_info.w2_scale,
a1_scale=a1_scale,
a2_scale=quant_info.a2_scale,
bias1=quant_info.b13,
bias2=quant_info.b2,
expert_mask=quant_info.expert_mask,
doweight_stage1=quant_info.doweight_stage1,
hidden_pad=quant_info.hidden_pad,
intermediate_pad=quant_info.intermediate_pad,
**extra,
)
return AiterRunnerOutput(hidden_states=output)
@property
def runner_backend(self) -> MoeRunnerBackend:
return MoeRunnerBackend.AITER
# ---------------------------------------------------------------------------
# Pre-permute: dispatch_output -> AiterRunnerInput
# ---------------------------------------------------------------------------
@register_pre_permute("standard", "aiter")
def pre_permute_standard_to_aiter(
dispatch_output: StandardDispatchOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
) -> StandardCombineInput:
from aiter import ActivationType, QuantType
from aiter.fused_moe import fused_moe
from sglang.srt.layers.moe.token_dispatcher.standard import StandardCombineInput
assert not runner_config.no_combine, "no_combine=True is not supported by AITER"
running_state: dict,
) -> AiterRunnerInput:
hidden_states = dispatch_output.hidden_states
topk_weights, topk_ids, _ = dispatch_output.topk_output
topk_weights = topk_weights.to(torch.float32)
@@ -71,24 +180,217 @@ def fused_experts_none_to_aiter(
hidden_states = hidden_states * topk_weights.to(hidden_states.dtype)
topk_weights = torch.ones_like(topk_weights)
activation = runner_config.activation
output = fused_moe(
return AiterRunnerInput(
hidden_states=hidden_states,
w1=quant_info.w13_weight,
w2=quant_info.w2_weight,
topk_weight=topk_weights,
topk_ids=topk_ids.to(torch.int32),
quant_type=getattr(QuantType, quant_info.quant_type.value),
activation=getattr(ActivationType, _AITER_ACTIVATIONS.get(activation, "Gelu")),
w1_scale=quant_info.w13_scale,
w2_scale=quant_info.w2_scale,
a1_scale=quant_info.a13_scale,
a2_scale=quant_info.a2_scale,
bias1=quant_info.b13,
bias2=quant_info.b2,
expert_mask=quant_info.expert_mask,
doweight_stage1=quant_info.doweight_stage1,
hidden_pad=quant_info.hidden_pad,
intermediate_pad=quant_info.intermediate_pad,
topk_weights=topk_weights,
quant_type=quant_info.quant_type,
)
def _is_mori_dispatch_output(dispatch_output: Any) -> bool:
# MoriEP{Normal,LL}DispatchOutput carry the post-mori-permute origin_topk_*
# tensors that the standard DeepEP outputs lack.
return hasattr(dispatch_output, "origin_topk_ids")
def _resolve_mori_quant_type(
dispatch_a1_dtype: torch.dtype,
dispatch_scale: Optional[torch.Tensor],
weight_quant: AiterQuantType,
) -> AiterQuantType:
"""Pick the activation quant_type for AITER when the dispatch path may have
pre-quantized hidden_states. Mirrors the original MoriEPMoE.run_moe_core
decision tree."""
is_fp8_quant = weight_quant in (
AiterQuantType.PER_128X128,
AiterQuantType.PER_TOKEN,
)
is_w4a4 = weight_quant == AiterQuantType.PER_1X32
is_fp4_dispatch = dispatch_a1_dtype == torch.float4_e2m1fn_x2
has_dispatch_scale = dispatch_scale is not None
if is_w4a4:
# W4A4 weights always run as per_1x32; FP8 dispatch is upscaled to BF16
# before this point so dispatch_scale won't conflict.
return AiterQuantType.PER_1X32
if is_fp8_quant:
return weight_quant
# BF16 weights: lift to the dispatch-side quant type when scales are provided.
if has_dispatch_scale and is_fp4_dispatch:
return AiterQuantType.PER_1X32
if has_dispatch_scale and not is_fp4_dispatch:
return AiterQuantType.PER_128X128
return AiterQuantType.NONE
def _pre_permute_deepep_to_aiter(
dispatch_output: Union[
DeepEPNormalDispatchOutput,
DeepEPLLDispatchOutput,
MoriEPNormalDispatchOutput,
MoriEPLLDispatchOutput,
],
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> AiterRunnerInput:
is_mori = _is_mori_dispatch_output(dispatch_output)
hidden_states = dispatch_output.hidden_states
topk_ids = dispatch_output.topk_ids.to(torch.int32)
topk_weights = dispatch_output.topk_weights.to(torch.float32)
a1_scale: Optional[torch.Tensor] = None
num_local_tokens: Optional[torch.Tensor] = None
output_dtype: Optional[torch.dtype] = None
quant_type = quant_info.quant_type
if is_mori:
from sglang.srt.layers.moe.rocm_moe_utils import upscale, upscale_mxfp4
a1_scale = dispatch_output.hidden_states_scale
num_local_tokens = dispatch_output.num_recv_tokens_per_expert
output_dtype = dispatch_output.out_dtype
# Truncate dispatch tensors to the configured cap; mori combine only
# reads [0, totalRecvTokenNum), so the truncated result needs no
# padding back.
mori_max = get_int_env_var("SGLANG_MORI_MOE_MAX_INPUT_TOKENS", 0)
if mori_max > 0:
hidden_states = hidden_states[:mori_max]
if a1_scale is not None:
a1_scale = a1_scale[:mori_max]
topk_ids = topk_ids[:mori_max]
topk_weights = topk_weights[:mori_max]
# Upscale dispatched activations when there is no AITER kernel for the
# weight/activation dtype pair.
weight_quant = quant_info.quant_type
is_fp8_quant = weight_quant in (
AiterQuantType.PER_128X128,
AiterQuantType.PER_TOKEN,
)
is_w4a4 = weight_quant == AiterQuantType.PER_1X32
is_fp4_dispatch = hidden_states.dtype == torch.float4_e2m1fn_x2
if is_w4a4 and a1_scale is not None and not is_fp4_dispatch:
# W4A4 weights with FP8 dispatch: dequant FP8->BF16 first; the
# FP4 per_1x32 path needs BF16 input.
hidden_states = upscale(
hidden_states, a1_scale, num_local_tokens, output_dtype
)
a1_scale = None
elif is_fp8_quant and is_fp4_dispatch and a1_scale is not None:
# FP8 weights + FP4 dispatch: no kernel for the fp4x2/fp8 pair;
# dequant FP4->BF16 and let fused_moe re-quantize to FP8.
hidden_states = upscale_mxfp4(
hidden_states, a1_scale, num_local_tokens, output_dtype
)
a1_scale = None
quant_type = _resolve_mori_quant_type(
hidden_states.dtype, a1_scale, weight_quant
)
running_state["aiter_combine_topk_ids"] = dispatch_output.origin_topk_ids
running_state["aiter_combine_topk_weights"] = (
dispatch_output.origin_topk_weights
)
else:
# DeepEP marks invalid topk slots with idx == -1; AITER cannot accept
# negative ids, so reroute them to the sink slot at index
# num_local_experts (masked off by quant_info.expert_mask which has
# shape (num_local_experts + 1,)).
topk_ids = torch.where(
topk_ids == -1,
torch.full_like(topk_ids, runner_config.num_local_experts),
topk_ids,
)
running_state["aiter_combine_topk_ids"] = dispatch_output.topk_ids
running_state["aiter_combine_topk_weights"] = dispatch_output.topk_weights
running_state["aiter_combine_is_mori"] = is_mori
return AiterRunnerInput(
hidden_states=hidden_states,
topk_ids=topk_ids,
topk_weights=topk_weights,
quant_type=quant_type,
a1_scale=a1_scale,
num_local_tokens=num_local_tokens,
output_dtype=output_dtype,
)
register_pre_permute("deepep_normal", "aiter")(_pre_permute_deepep_to_aiter)
register_pre_permute("deepep_ll", "aiter")(_pre_permute_deepep_to_aiter)
# ---------------------------------------------------------------------------
# Post-permute: AiterRunnerOutput -> CombineInput
# ---------------------------------------------------------------------------
@register_post_permute("aiter", "standard")
def post_permute_aiter_to_standard(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> StandardCombineInput:
from sglang.srt.layers.moe.token_dispatcher.standard import StandardCombineInput
return StandardCombineInput(hidden_states=runner_output.hidden_states)
def _post_permute_aiter_to_deepep(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
is_normal: bool,
) -> CombineInput:
if running_state.get("aiter_combine_is_mori"):
from sglang.srt.layers.moe.token_dispatcher.moriep import (
MoriEPLLCombineInput,
MoriEPNormalCombineInput,
)
cls = MoriEPNormalCombineInput if is_normal else MoriEPLLCombineInput
else:
from sglang.srt.layers.moe.token_dispatcher.deepep import (
DeepEPLLCombineInput,
DeepEPNormalCombineInput,
)
cls = DeepEPNormalCombineInput if is_normal else DeepEPLLCombineInput
return cls(
hidden_states=runner_output.hidden_states,
topk_ids=running_state["aiter_combine_topk_ids"],
topk_weights=running_state["aiter_combine_topk_weights"],
)
@register_post_permute("aiter", "deepep_normal")
def post_permute_aiter_to_deepep_normal(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> CombineInput:
return _post_permute_aiter_to_deepep(
runner_output, quant_info, runner_config, running_state, is_normal=True
)
@register_post_permute("aiter", "deepep_ll")
def post_permute_aiter_to_deepep_ll(
runner_output: AiterRunnerOutput,
quant_info: AiterMoeQuantInfo,
runner_config: MoeRunnerConfig,
running_state: dict,
) -> CombineInput:
return _post_permute_aiter_to_deepep(
runner_output, quant_info, runner_config, running_state, is_normal=False
)
return StandardCombineInput(hidden_states=output)
@@ -44,10 +44,9 @@ class MoeRunner:
elif runner_backend.is_deep_gemm():
self.runner_core = DeepGemmRunnerCore(config)
elif runner_backend.is_aiter():
# Side-effect import: registers the ("none", "aiter") fused func.
from sglang.srt.layers.moe.moe_runner import aiter # noqa: F401
from sglang.srt.layers.moe.moe_runner.aiter import AiterRunnerCore
self.runner_core = None # AITER only supports fused path
self.runner_core = AiterRunnerCore(config)
elif runner_backend.is_marlin():
if lora_enabled:
from sglang.srt.lora.lora_moe_runner_marlin import MarlinLoraRunnerCore
@@ -816,6 +816,19 @@ class DeepEPDispatcher(BaseDispatcher):
self._stage = _Stage.INITIAL
self._deepep_dispatch_hooks = DeepEPPDispatchHooks()
# DeepEP/Mooncake/Nixl mark invalid topk slots with -1; the AITER
# pre_permute reroutes them to a sink slot at index num_local_experts,
# which is masked off here.
self.expert_mask_gpu = None
if _use_aiter and num_local_experts is not None:
expert_mask = torch.zeros(
num_local_experts + 1,
device=torch.cuda.current_device(),
dtype=torch.int,
)
expert_mask[:-1] = 1
self.expert_mask_gpu = expert_mask
def dispatch(
self,
hidden_states: torch.Tensor,
@@ -1011,11 +1011,26 @@ class MoriEPDispatcher(BaseDispatcher):
self._stage = _Stage.INITIAL
self._deepep_dispatch_hooks = MoriEPPDispatchHooks()
# Mori dispatch produces global topk_ids in [0, num_experts); mask out
# experts that are not local to this rank.
self.expert_mask_gpu = None
if _use_aiter and num_experts is not None and num_local_experts is not None:
ep_rank = get_moe_expert_parallel_rank()
expert_mask = torch.zeros(
num_experts,
device=torch.cuda.current_device(),
dtype=torch.int32,
)
start = ep_rank * num_local_experts
expert_mask[start : start + num_local_experts] = 1
self.expert_mask_gpu = expert_mask
def dispatch(
self,
hidden_states: torch.Tensor,
topk_output: TopKOutput,
) -> DispatchOutput:
self._num_tokens = hidden_states.shape[0]
self.dispatch_a(hidden_states, topk_output)
if self._deepep_dispatch_hooks is not None:
self._deepep_dispatch_hooks(self)
@@ -1045,8 +1060,8 @@ class MoriEPDispatcher(BaseDispatcher):
combine_input: CombineInput,
) -> Tuple:
self.combine_a(combine_input)
ret = self.combine_b()
return ret
hidden_states = self.combine_b()
return hidden_states[: self._num_tokens]
def combine_a(
self,
+9
View File
@@ -68,6 +68,15 @@ class MoeA2ABackend(Enum):
def is_customized(self):
return self == MoeA2ABackend.CUSTOMIZED
def supports_aiter(self) -> bool:
return self in (
MoeA2ABackend.NONE,
MoeA2ABackend.DEEPEP,
MoeA2ABackend.MOONCAKE,
MoeA2ABackend.NIXL,
MoeA2ABackend.MORI,
)
class MoeRunnerBackend(Enum):
@@ -349,7 +349,7 @@ class CompressedTensorsW8A8Fp8MoE(CompressedTensorsMoEScheme):
if (
_use_aiter
and self.weight_quant.strategy == QuantizationStrategy.CHANNEL
and get_moe_a2a_backend().is_none()
and get_moe_a2a_backend().supports_aiter()
):
moe_runner_backend = MoeRunnerBackend.AITER
else:
+1 -3
View File
@@ -1640,10 +1640,8 @@ class Fp8MoEMethod(FusedMoEMethodBase):
elif (
_is_hip
and (_use_aiter or _use_hip_int4)
and get_moe_a2a_backend().is_none()
and get_moe_a2a_backend().supports_aiter()
):
# *EPMoE backends bypass self.runner via run_moe_core, and the
# AITER fused func is only registered for ("none", "aiter").
moe_runner_backend = MoeRunnerBackend.AITER
else:
moe_runner_backend = MoeRunnerBackend.TRITON
@@ -1006,7 +1006,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
moe_runner_backend = get_moe_runner_backend()
if moe_runner_backend.is_auto():
# Must match apply() priority: _use_aiter before use_triton_kernels.
if _use_aiter and get_moe_a2a_backend().is_none():
if _use_aiter and get_moe_a2a_backend().supports_aiter():
moe_runner_backend = MoeRunnerBackend.AITER
elif self.use_triton_kernels:
moe_runner_backend = MoeRunnerBackend.TRITON_KERNELS
@@ -1360,7 +1360,7 @@ class Mxfp4DynamicQuantMoEMethod(FusedMoEMethodBase):
):
self.moe_runner_config = moe_runner_config
moe_runner_backend = get_moe_runner_backend()
if moe_runner_backend.is_auto() and get_moe_a2a_backend().is_none():
if moe_runner_backend.is_auto() and get_moe_a2a_backend().supports_aiter():
moe_runner_backend = MoeRunnerBackend.AITER
if moe_runner_backend.is_aiter():
@@ -187,7 +187,7 @@ class QuarkW4A4MXFp4MoE(QuarkMoEScheme):
self.moe_runner_config = moe_runner_config
moe_runner_backend = get_moe_runner_backend()
if moe_runner_backend.is_auto() and get_moe_a2a_backend().is_none():
if moe_runner_backend.is_auto() and get_moe_a2a_backend().supports_aiter():
moe_runner_backend = MoeRunnerBackend.AITER
if moe_runner_backend.is_aiter():
@@ -410,7 +410,7 @@ class QuarkInt4Fp8MoEMethod(FusedMoEMethodBase):
self.moe_runner_config = moe_runner_config
moe_runner_backend = get_moe_runner_backend()
if moe_runner_backend.is_auto() and get_moe_a2a_backend().is_none():
if moe_runner_backend.is_auto() and get_moe_a2a_backend().supports_aiter():
moe_runner_backend = MoeRunnerBackend.AITER
if moe_runner_backend.is_aiter():
@@ -396,7 +396,7 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, MultiPlatformOp):
get_moe_runner_backend().is_auto()
or get_moe_runner_backend().is_aiter()
)
and get_moe_a2a_backend().is_none()
and get_moe_a2a_backend().supports_aiter()
):
self._aiter_runner = MoeRunner(MoeRunnerBackend.AITER, moe_runner_config)
@@ -489,7 +489,9 @@ class UnquantizedFusedMoEMethod(FusedMoEMethodBase, MultiPlatformOp):
return self.runner.run(dispatch_output, quant_info)
else:
if self._aiter_runner is not None:
from sglang.srt.layers.moe.moe_runner.aiter import AiterMoeQuantInfo
from sglang.srt.layers.moe.moe_runner.aiter import (
AiterMoeQuantInfo,
)
try:
quant_info = AiterMoeQuantInfo(