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
567 lines
19 KiB
Python
567 lines
19 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import os
|
|
from contextlib import contextmanager
|
|
from enum import Enum, IntEnum
|
|
from typing import TYPE_CHECKING
|
|
|
|
import torch
|
|
|
|
from sglang.srt.environ import envs
|
|
from sglang.srt.layers.dp_attention import (
|
|
is_dp_attention_enabled,
|
|
)
|
|
from sglang.srt.runtime_context import get_flags, get_forward, get_parallel
|
|
from sglang.srt.utils import is_cuda, is_npu
|
|
|
|
_is_npu = is_npu()
|
|
|
|
if TYPE_CHECKING:
|
|
from sglang.srt.server_args import ServerArgs
|
|
|
|
from sglang.srt.runtime_context import get_server_args
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class MoeA2ABackend(Enum):
|
|
|
|
NONE = "none"
|
|
DEEPEP = "deepep"
|
|
MOONCAKE = "mooncake"
|
|
NIXL = "nixl"
|
|
MORI = "mori"
|
|
ASCEND_FUSEEP = "ascend_fuseep"
|
|
FLASHINFER = "flashinfer"
|
|
MEGAMOE = "megamoe"
|
|
CUSTOMIZED = "customized"
|
|
|
|
@classmethod
|
|
def _missing_(cls, value):
|
|
if value is None:
|
|
return cls.NONE
|
|
for member in cls:
|
|
if value == member.value:
|
|
return member
|
|
raise ValueError(f"No {cls.__name__} member for value {value}")
|
|
|
|
def is_none(self):
|
|
return self == MoeA2ABackend.NONE
|
|
|
|
def is_deepep(self):
|
|
return self == MoeA2ABackend.DEEPEP
|
|
|
|
def is_mooncake(self):
|
|
return self == MoeA2ABackend.MOONCAKE
|
|
|
|
def is_nixl(self):
|
|
return self == MoeA2ABackend.NIXL
|
|
|
|
def is_flashinfer(self):
|
|
return self == MoeA2ABackend.FLASHINFER
|
|
|
|
def is_ascend_fuseep(self):
|
|
return self == MoeA2ABackend.ASCEND_FUSEEP
|
|
|
|
def is_mori(self):
|
|
return self == MoeA2ABackend.MORI
|
|
|
|
def is_megamoe(self):
|
|
return self == MoeA2ABackend.MEGAMOE
|
|
|
|
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):
|
|
|
|
AUTO = "auto"
|
|
DEEP_GEMM = "deep_gemm"
|
|
TRITON = "triton"
|
|
TRITON_KERNELS = "triton_kernel"
|
|
FLASHINFER_TRTLLM = "flashinfer_trtllm"
|
|
EXPERIMENTAL_SGL_TRTLLM = "experimental_sgl_trtllm"
|
|
FLASHINFER_TRTLLM_ROUTED = "flashinfer_trtllm_routed"
|
|
FLASHINFER_CUTLASS = "flashinfer_cutlass"
|
|
FLASHINFER_MXFP4 = "flashinfer_mxfp4"
|
|
FLASHINFER_CUTEDSL = "flashinfer_cutedsl"
|
|
CUTLASS = "cutlass"
|
|
MARLIN = "marlin"
|
|
AITER = "aiter"
|
|
|
|
def is_auto(self):
|
|
return self == MoeRunnerBackend.AUTO
|
|
|
|
def is_deep_gemm(self):
|
|
return self == MoeRunnerBackend.DEEP_GEMM
|
|
|
|
def is_triton(self):
|
|
return self == MoeRunnerBackend.TRITON
|
|
|
|
def is_triton_kernels(self):
|
|
return self == MoeRunnerBackend.TRITON_KERNELS
|
|
|
|
def is_flashinfer_trtllm(self):
|
|
# experimental_sgl_trtllm shares the TRT-LLM FP8 kernels + layout, so it inherits
|
|
# trtllm weight-prep here; divergent sites check is_experimental_sgl_trtllm() first.
|
|
return self in (
|
|
MoeRunnerBackend.FLASHINFER_TRTLLM,
|
|
MoeRunnerBackend.EXPERIMENTAL_SGL_TRTLLM,
|
|
)
|
|
|
|
def is_experimental_sgl_trtllm(self):
|
|
return self == MoeRunnerBackend.EXPERIMENTAL_SGL_TRTLLM
|
|
|
|
def is_flashinfer_trtllm_routed(self):
|
|
return self == MoeRunnerBackend.FLASHINFER_TRTLLM_ROUTED
|
|
|
|
def is_flashinfer_cutlass(self):
|
|
return self == MoeRunnerBackend.FLASHINFER_CUTLASS
|
|
|
|
def is_flashinfer_cutedsl(self):
|
|
return self == MoeRunnerBackend.FLASHINFER_CUTEDSL
|
|
|
|
def is_flashinfer_mxfp4(self):
|
|
return self == MoeRunnerBackend.FLASHINFER_MXFP4
|
|
|
|
def is_cutlass(self):
|
|
return self == MoeRunnerBackend.CUTLASS
|
|
|
|
def is_marlin(self):
|
|
return self == MoeRunnerBackend.MARLIN
|
|
|
|
def is_aiter(self):
|
|
return self == MoeRunnerBackend.AITER
|
|
|
|
|
|
class DeepEPMode(Enum):
|
|
|
|
NORMAL = "normal"
|
|
LOW_LATENCY = "low_latency"
|
|
AUTO = "auto"
|
|
|
|
def enable_normal(self) -> bool:
|
|
return self in [DeepEPMode.NORMAL, DeepEPMode.AUTO]
|
|
|
|
def enable_low_latency(self) -> bool:
|
|
return self in [DeepEPMode.LOW_LATENCY, DeepEPMode.AUTO]
|
|
|
|
def resolve(self, is_extend_in_batch: bool) -> DeepEPMode:
|
|
if self != DeepEPMode.AUTO:
|
|
return self
|
|
|
|
if is_extend_in_batch:
|
|
return DeepEPMode.NORMAL
|
|
else:
|
|
return DeepEPMode.LOW_LATENCY
|
|
|
|
def is_normal(self) -> bool:
|
|
return self == DeepEPMode.NORMAL
|
|
|
|
def is_low_latency(self) -> bool:
|
|
return self == DeepEPMode.LOW_LATENCY
|
|
|
|
def is_auto(self) -> bool:
|
|
return self == DeepEPMode.AUTO
|
|
|
|
|
|
class DeepEPOutputDtype(Enum):
|
|
"""
|
|
Describes the dispatch output data type for DeepEP.
|
|
|
|
- BF16: dispatch hidden states in bf16
|
|
- FP8: dispatch hidden states in fp8
|
|
- INT8: dispatch hidden states in int8
|
|
- NVFP4: dispatch hidden states in nvfp4
|
|
"""
|
|
|
|
BF16 = "bf16"
|
|
FP8 = "fp8"
|
|
INT8 = "int8"
|
|
NVFP4 = "nvfp4"
|
|
|
|
|
|
def get_deepep_output_dtype(self) -> DeepEPOutputDtype:
|
|
"""
|
|
Automatically choose the dispatch output dtype for DeepEP.
|
|
|
|
The decision follows several checks in priority order:
|
|
0. Parse server argument.
|
|
1. Parse deprecated environment variables.
|
|
2. If quant_config contains input_global_scale → NVFP4 path.
|
|
3. Parse quant config
|
|
4. If flashinfer_cutedsl or is_cutlass backend is active → BF16 (it quantizes hidden_states internally).
|
|
5. Otherwise default for NPU → BF16 (the default for NPU).
|
|
6. Otherwise → FP8 (the default for most models like DeepSeek-V3).
|
|
"""
|
|
|
|
# 0. Parse server argument.
|
|
server_args = get_server_args()
|
|
if server_args and server_args.deepep_dispatcher_output_dtype != "auto":
|
|
return DeepEPOutputDtype(server_args.deepep_dispatcher_output_dtype)
|
|
|
|
# 1. Parse deprecated environment variables.
|
|
if envs.SGLANG_DEEPEP_BF16_DISPATCH.get():
|
|
logger.warning_once(
|
|
"Warning: The env variable SGLANG_DEEPEP_BF16_DISPATCH deprecated "
|
|
"and will be removed in future releases. Please use a new "
|
|
"`--deepep-dispatcher-output-dtype bf16` argument instead."
|
|
)
|
|
return DeepEPOutputDtype.BF16
|
|
|
|
# 2. NVFP4 is detected inside dispatch_a / _dispatch_core via quant_config; no need to infer here.
|
|
if self.quant_config is not None:
|
|
input_global_scale = self.quant_config.get("input_global_scale", None)
|
|
if input_global_scale is not None:
|
|
return DeepEPOutputDtype.NVFP4
|
|
|
|
# 3. Parse quant config to determine the output dtype of dispatcher
|
|
dispatcher_output_dtype = self.quant_config.get("dispatcher_output_dtype", None)
|
|
if dispatcher_output_dtype is not None:
|
|
return DeepEPOutputDtype(dispatcher_output_dtype)
|
|
|
|
# 4. flashinfer_cutedsl and is_cutlass expects BF16 dispatch
|
|
if (
|
|
get_moe_runner_backend().is_flashinfer_cutedsl()
|
|
or get_moe_runner_backend().is_cutlass()
|
|
):
|
|
return DeepEPOutputDtype.BF16
|
|
|
|
# 5. Default on NPU → BF16
|
|
if _is_npu:
|
|
return DeepEPOutputDtype.BF16
|
|
|
|
# 6. Default → FP8
|
|
return DeepEPOutputDtype.FP8
|
|
|
|
|
|
def initialize_moe_config(server_args: ServerArgs):
|
|
moe = get_flags().moe
|
|
moe.a2a_backend = MoeA2ABackend(server_args.moe_a2a_backend)
|
|
moe.runner_backend = MoeRunnerBackend(server_args.moe_runner_backend)
|
|
moe.speculative_runner_backend = (
|
|
MoeRunnerBackend(server_args.speculative_moe_runner_backend)
|
|
if server_args.speculative_moe_runner_backend is not None
|
|
else moe.runner_backend
|
|
)
|
|
moe.speculative_a2a_backend = (
|
|
MoeA2ABackend(server_args.speculative_moe_a2a_backend)
|
|
if server_args.speculative_moe_a2a_backend is not None
|
|
else moe.a2a_backend
|
|
)
|
|
moe.deepep_mode = DeepEPMode(server_args.deepep_mode)
|
|
moe.deepep_config = server_args.deepep_config or ""
|
|
moe.tbo_enabled = server_args.enable_two_batch_overlap
|
|
moe.sbo_enabled = server_args.enable_single_batch_overlap
|
|
if moe.sbo_enabled and is_cuda():
|
|
if torch.cuda.get_device_capability()[0] == 9:
|
|
raise ValueError(
|
|
"SBO (single batch overlap) is not supported on SM90 GPUs with latest sgl-deep-gemm wheel. Please try removing --enable-single-batch-overlap argument."
|
|
)
|
|
moe.tbo_token_distribution_threshold = server_args.tbo_token_distribution_threshold
|
|
moe.disable_fp4_allgather = server_args.disable_flashinfer_cutlass_moe_fp4_allgather
|
|
moe.quantization = server_args.quantization
|
|
|
|
|
|
def get_moe_a2a_backend() -> MoeA2ABackend:
|
|
moe = get_flags().moe
|
|
if moe.a2a_backend is None:
|
|
moe.a2a_backend = MoeA2ABackend.NONE
|
|
return moe.a2a_backend
|
|
|
|
|
|
def get_moe_runner_backend() -> MoeRunnerBackend:
|
|
moe = get_flags().moe
|
|
if moe.runner_backend is None:
|
|
moe.runner_backend = MoeRunnerBackend.AUTO
|
|
return moe.runner_backend
|
|
|
|
|
|
def get_speculative_moe_runner_backend() -> MoeRunnerBackend:
|
|
moe = get_flags().moe
|
|
if moe.speculative_runner_backend is None:
|
|
logger.warning(
|
|
"SPECULATIVE_MOE_RUNNER_BACKEND is not initialized, using auto backend"
|
|
)
|
|
moe.speculative_runner_backend = MoeRunnerBackend.AUTO
|
|
return moe.speculative_runner_backend
|
|
|
|
|
|
def get_speculative_moe_a2a_backend() -> MoeA2ABackend:
|
|
moe = get_flags().moe
|
|
if moe.speculative_a2a_backend is None:
|
|
logger.warning(
|
|
"SPECULATIVE_MOE_A2A_BACKEND is not initialized, using none backend"
|
|
)
|
|
moe.speculative_a2a_backend = MoeA2ABackend.NONE
|
|
return moe.speculative_a2a_backend
|
|
|
|
|
|
def get_deepep_mode() -> DeepEPMode:
|
|
moe = get_flags().moe
|
|
if moe.deepep_mode is None:
|
|
logger.warning("DEEPEP_MODE is not initialized, using auto mode")
|
|
moe.deepep_mode = DeepEPMode.AUTO
|
|
return moe.deepep_mode
|
|
|
|
|
|
def get_deepep_config() -> str:
|
|
moe = get_flags().moe
|
|
if moe.deepep_config is None:
|
|
logger.warning("DEEPEP_CONFIG is not initialized, using default config")
|
|
moe.deepep_config = ""
|
|
return moe.deepep_config
|
|
|
|
|
|
def is_tbo_enabled() -> bool:
|
|
moe = get_flags().moe
|
|
if moe.tbo_enabled is None:
|
|
moe.tbo_enabled = False
|
|
return moe.tbo_enabled
|
|
|
|
|
|
def is_sbo_enabled() -> bool:
|
|
moe = get_flags().moe
|
|
if moe.sbo_enabled is None:
|
|
moe.sbo_enabled = False
|
|
return moe.sbo_enabled
|
|
|
|
|
|
def is_deepep_class_backend() -> bool:
|
|
"""Check if the MoE backend is DeepEP-family (DeepEP, Mooncake, or Mori)."""
|
|
b = get_moe_a2a_backend()
|
|
return b.is_deepep() or b.is_mooncake() or b.is_mori()
|
|
|
|
|
|
def uses_per_rank_fused_shared_slots() -> bool:
|
|
"""Check whether fused shared experts use per-rank physical slots."""
|
|
return is_deepep_class_backend() or get_moe_a2a_backend().is_megamoe()
|
|
|
|
|
|
def has_per_rank_fused_shared_slots(num_fused_shared_experts: int) -> bool:
|
|
"""Check whether this layer has fused shared experts in per-rank slots."""
|
|
return num_fused_shared_experts > 0 and uses_per_rank_fused_shared_slots()
|
|
|
|
|
|
def is_flashinfer_cutedsl_v1_path() -> bool:
|
|
"""CuteDSL v1 + DeepEP low-latency path (no MoeRunner, no autotune)."""
|
|
return (
|
|
get_moe_runner_backend().is_flashinfer_cutedsl()
|
|
and get_moe_a2a_backend().is_deepep()
|
|
)
|
|
|
|
|
|
def get_tbo_token_distribution_threshold() -> float:
|
|
moe = get_flags().moe
|
|
if moe.tbo_token_distribution_threshold is None:
|
|
logger.warning(
|
|
"TBO_TOKEN_DISTRIBUTION_THRESHOLD is not initialized, using 0.48"
|
|
)
|
|
moe.tbo_token_distribution_threshold = 0.48
|
|
return moe.tbo_token_distribution_threshold
|
|
|
|
|
|
def filter_moe_weight_param_global_expert(name, x, num_local_experts):
|
|
"""
|
|
Filter out for MoE expert parameters that requires global expert.
|
|
"""
|
|
return (
|
|
not getattr(x, "_sglang_require_global_experts", False)
|
|
and x.data.ndim > 0
|
|
and x.data.shape[0] == num_local_experts
|
|
)
|
|
|
|
|
|
def should_use_flashinfer_cutlass_moe_fp4_allgather():
|
|
"""
|
|
Perform FP4 quantize before all-gather for flashinfer cutlass moe to reduce communication cost for high-throughput serving.
|
|
"""
|
|
return (
|
|
not get_flags().moe.disable_fp4_allgather
|
|
and get_moe_a2a_backend().is_none()
|
|
and get_moe_runner_backend().is_flashinfer_cutlass()
|
|
and is_dp_attention_enabled()
|
|
and get_flags().moe.quantization == "modelopt_fp4"
|
|
and get_parallel().moe_ep_size == get_parallel().attn_dp_size
|
|
)
|
|
|
|
|
|
def should_use_dp_reduce_scatterv():
|
|
"""
|
|
Use reduce_scatterv in the standard dispatcher's combine() for DP attention
|
|
with EP, replacing the default all-reduce + dp_scatter path.
|
|
Only changes the combine (post-kernel) communication; dispatch is unchanged.
|
|
"""
|
|
return (
|
|
not should_use_flashinfer_cutlass_moe_fp4_allgather()
|
|
and get_moe_a2a_backend().is_none()
|
|
and is_dp_attention_enabled()
|
|
and get_parallel().attn_dp_size > 1
|
|
and get_parallel().moe_ep_size == get_parallel().attn_dp_size
|
|
)
|
|
|
|
|
|
def should_skip_mlp_all_reduce() -> bool:
|
|
"""Whether dense MLP / row-parallel projections should skip their all-reduce.
|
|
|
|
True when the decoder published ``fuse_mlp_allreduce`` (next residual+LN
|
|
absorbs the AR) or ``mlp_reduce_scatter`` (postprocess will reduce-scatter)
|
|
on ``get_forward()``.
|
|
"""
|
|
f = get_forward()
|
|
return f.fuse_mlp_allreduce or f.mlp_reduce_scatter
|
|
|
|
|
|
def should_skip_post_experts_all_reduce(*, is_tp_path: bool) -> bool:
|
|
"""Whether to skip the post-experts all-reduce (EP or TP) because a
|
|
downstream component will fuse, replace, or absorb it.
|
|
|
|
Skip reasons, in order:
|
|
- ``get_forward().fuse_mlp_allreduce``: LayerCommunicator will fuse the
|
|
all-reduce with the next layer's residual all-reduce.
|
|
- ``get_forward().mlp_reduce_scatter``: LayerCommunicator's post-attention
|
|
scatter will do reduce-scatter, which would double-reduce on top of
|
|
an all-reduce.
|
|
- ``should_use_dp_reduce_scatterv()``: the standard dispatcher's combine
|
|
path replaces the all-reduce with a reduce-scatterv.
|
|
- ``should_use_flashinfer_cutlass_moe_fp4_allgather()`` (TP path only):
|
|
the flashinfer cutlass FP4 kernel performs an all-gather that absorbs
|
|
the post-experts TP all-reduce. Not relevant to the EP all-reduce.
|
|
- ``get_moe_a2a_backend().is_flashinfer()``: the flashinfer A2A
|
|
dispatcher's ``MoeAlltoAll.combine`` already alltoall-reduces partial
|
|
MoE outputs back to the source rank, so any further EP/TP all-reduce
|
|
would double-count and overflow BF16. Mirrors TRTLLM's
|
|
``not enable_alltoall`` gate
|
|
(``tensorrt_llm/_torch/modules/fused_moe/interface.py:879``).
|
|
|
|
The first two reasons come from per-layer ``ForwardFlags`` published by
|
|
the decoder via ``get_forward().scoped(...)``. Pass ``is_tp_path=True``
|
|
for the post-experts TP all-reduce, ``False`` for the EP all-reduce.
|
|
"""
|
|
if should_skip_mlp_all_reduce():
|
|
return True
|
|
if should_use_dp_reduce_scatterv():
|
|
return True
|
|
if is_tp_path and should_use_flashinfer_cutlass_moe_fp4_allgather():
|
|
return True
|
|
if get_moe_a2a_backend().is_flashinfer():
|
|
return True
|
|
return False
|
|
|
|
|
|
@contextmanager
|
|
def speculative_moe_backend_context():
|
|
"""
|
|
Context manager to temporarily use the speculative MoE backend for draft model operations.
|
|
This ensures that draft models in speculative decoding use the configured speculative backend.
|
|
"""
|
|
moe = get_flags().moe
|
|
original_backend = moe.runner_backend
|
|
try:
|
|
moe.runner_backend = get_speculative_moe_runner_backend()
|
|
yield
|
|
finally:
|
|
moe.runner_backend = original_backend
|
|
|
|
|
|
@contextmanager
|
|
def speculative_moe_a2a_backend_context():
|
|
"""
|
|
Context manager to temporarily use the speculative MoE A2A backend for draft model operations.
|
|
This ensures that draft models in speculative decoding use the configured speculative A2A backend.
|
|
"""
|
|
moe = get_flags().moe
|
|
original_backend = moe.a2a_backend
|
|
original_disable_fp4_allgather = moe.disable_fp4_allgather
|
|
try:
|
|
moe.a2a_backend = get_speculative_moe_a2a_backend()
|
|
# Disable FP4 allgather for spec decode since MTP layers are unquantized
|
|
moe.disable_fp4_allgather = True
|
|
yield
|
|
finally:
|
|
moe.a2a_backend = original_backend
|
|
moe.disable_fp4_allgather = original_disable_fp4_allgather
|
|
|
|
|
|
# The type of method in top-K routing, for use in torch custom op
|
|
# Please keep this in sync with the counterpart defined in https://github.com/flashinfer-ai/flashinfer/blob/main/include/flashinfer/trtllm/fused_moe/runner.h
|
|
class RoutingMethodType(IntEnum):
|
|
# Default: Softmax -> TopK
|
|
Default = (0,)
|
|
# Renormalize: TopK -> Softmax
|
|
Renormalize = (1,)
|
|
# DeepSeekV3: Sigmoid -> RoutingBiasAdd -> Top2 in group -> Top4 groups -> Top8 experts from the Top4 groups
|
|
DeepSeekV3 = (2,)
|
|
# Llama4: Top1 -> Sigmoid
|
|
Llama4 = (3,)
|
|
# Qwen3: Softmax -> TopK -> Renormalize
|
|
RenormalizeNaive = (4,)
|
|
# TopK only (no softmax)
|
|
TopK = (5,)
|
|
# SigmoidRenorm: Sigmoid -> TopK -> Renormalize
|
|
SigmoidRenorm = (6,)
|
|
# MiniMax2
|
|
MiniMax2 = (7,)
|
|
# Sigmoid: Sigmoid -> TopK (no renormalize)
|
|
Sigmoid = (8,)
|
|
# Unspecified
|
|
Unspecified = 9
|
|
|
|
|
|
AITER_PADDING_SIZE = 128
|
|
TRITON_PADDING_SIZE = 128
|
|
|
|
|
|
# Unit of padding - context dependent
|
|
def get_moe_padding_size(is_aiter_moe):
|
|
if is_aiter_moe:
|
|
return AITER_PADDING_SIZE
|
|
else:
|
|
return (
|
|
TRITON_PADDING_SIZE
|
|
if bool(int(os.getenv("SGLANG_MOE_PADDING", "0")))
|
|
else 0
|
|
)
|
|
|
|
|
|
def get_moe_weight_sizes(inter_dim, is_concat, is_packed, is_aiter_moe):
|
|
"""
|
|
Calculate dimensions for MoE weight tensors.
|
|
|
|
Args:
|
|
inter_dim: Base intermediate dimension.
|
|
is_concat: If True, fusions W1 (gate) and W3 (up) projections.
|
|
is_packed: If True, uses 4-bit quantization (two FP4 elements per byte).
|
|
is_aiter_moe: If True, applies Aiter-specific kernel padding alignment.
|
|
"""
|
|
# w2_down_dim is the packing rank, but w13_up_dim not (of matrix to matmul)
|
|
w13_up_dim = 2 * inter_dim if is_concat else inter_dim
|
|
w2_down_dim = inter_dim // 2 if is_packed else inter_dim
|
|
|
|
if is_aiter_moe:
|
|
padding_size = get_moe_padding_size(True)
|
|
align_aiter = lambda n: ((n + padding_size - 1) // padding_size) * padding_size
|
|
is_padded = (w2_down_dim % padding_size) > 0
|
|
if is_padded:
|
|
# w2_down_dim, padding & aligned, unit: parameter dtype
|
|
w2_down_dim = align_aiter(w2_down_dim)
|
|
# up proj + gate fusion : 2x
|
|
if is_concat:
|
|
w13_up_dim = w2_down_dim * 2
|
|
# packed
|
|
if hasattr(torch, "float4_e2m1fn_x2") and is_packed:
|
|
# w13_up_dim (row rank of matmul matrix) is not packing dim, *2 to recover
|
|
w13_up_dim *= 2
|
|
|
|
return (w13_up_dim, w2_down_dim, False if not is_aiter_moe else is_padded)
|