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
365 lines
12 KiB
Python
365 lines
12 KiB
Python
# Copyright 2023-2024 SGLang Team
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
# ==============================================================================
|
|
"""Mega-MoE forward path and expert-weight prep shared by Deepseek V2/V4."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from contextlib import nullcontext
|
|
from typing import TYPE_CHECKING, Optional
|
|
|
|
import torch
|
|
|
|
from sglang.jit_kernel.dsv4 import mega_moe_pre_dispatch
|
|
from sglang.srt.environ import envs
|
|
from sglang.srt.eplb.expert_location_dispatch import ExpertLocationDispatchInfo
|
|
from sglang.srt.layers.dp_attention import get_dp_global_num_tokens
|
|
from sglang.srt.layers.moe.utils import get_moe_a2a_backend
|
|
from sglang.srt.model_executor.runner import get_is_capture_mode
|
|
|
|
if TYPE_CHECKING:
|
|
from deep_gemm import SymmBuffer
|
|
|
|
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
|
|
from sglang.srt.models.deepseek_v2 import DeepseekV2MoE
|
|
|
|
|
|
_MEGA_MOE_SYMM_BUFFER: dict = {}
|
|
_MEGA_MOE_DG_ENV_APPLIED = False
|
|
|
|
|
|
def _apply_mega_moe_dg_env() -> None:
|
|
"""Forward sglang's FP4/MXF4 opt-in flags to DeepGEMM via env vars.
|
|
|
|
DeepGEMM reads `DG_USE_FP4_ACTS` (and `DG_USE_MXF4_KIND`) at host-function
|
|
call time — both `get_symm_buffer_for_mega_moe` and `fp8_fp4_mega_moe`.
|
|
Forwarding once at first use is sufficient (these are static config
|
|
flags, not per-request state) and matches the `setdefault` pattern so
|
|
explicit `DG_USE_*` overrides from outside still win.
|
|
"""
|
|
global _MEGA_MOE_DG_ENV_APPLIED
|
|
if _MEGA_MOE_DG_ENV_APPLIED:
|
|
return
|
|
if envs.SGLANG_OPT_DEEPGEMM_MEGA_MOE_USE_FP4_ACTS.get():
|
|
os.environ.setdefault("DG_USE_FP4_ACTS", "1")
|
|
if envs.SGLANG_OPT_DEEPGEMM_MEGA_MOE_USE_MXF4_KIND.get():
|
|
os.environ.setdefault("DG_USE_MXF4_KIND", "1")
|
|
_MEGA_MOE_DG_ENV_APPLIED = True
|
|
|
|
|
|
def _get_mega_moe_symm_buffer(
|
|
group,
|
|
num_experts: int,
|
|
num_max_tokens_per_rank: int,
|
|
num_topk: int,
|
|
hidden: int,
|
|
intermediate_hidden: int,
|
|
) -> SymmBuffer:
|
|
import deep_gemm
|
|
|
|
_apply_mega_moe_dg_env()
|
|
|
|
key = (
|
|
id(group),
|
|
num_max_tokens_per_rank,
|
|
num_experts,
|
|
num_topk,
|
|
hidden,
|
|
intermediate_hidden,
|
|
)
|
|
buf = _MEGA_MOE_SYMM_BUFFER.get(key)
|
|
if buf is None:
|
|
buf = deep_gemm.get_symm_buffer_for_mega_moe(
|
|
group,
|
|
num_experts,
|
|
num_max_tokens_per_rank,
|
|
num_topk,
|
|
hidden,
|
|
intermediate_hidden,
|
|
use_fp8_dispatch=True,
|
|
activation="swiglu",
|
|
)
|
|
_MEGA_MOE_SYMM_BUFFER[key] = buf
|
|
return buf
|
|
|
|
|
|
def should_use_mega_moe(moe: DeepseekV2MoE, hidden_states: torch.Tensor) -> bool:
|
|
if not get_moe_a2a_backend().is_megamoe():
|
|
return False
|
|
if not getattr(moe.experts, "_mega_moe_weights_built", False):
|
|
return False
|
|
if get_is_capture_mode():
|
|
return True
|
|
|
|
global_num_tokens = get_dp_global_num_tokens()
|
|
if global_num_tokens:
|
|
max_tokens_per_rank = max(global_num_tokens)
|
|
else:
|
|
max_tokens_per_rank = hidden_states.shape[0]
|
|
cap = envs.SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK.get()
|
|
return max_tokens_per_rank <= cap
|
|
|
|
|
|
def forward_mega_moe(
|
|
moe: DeepseekV2MoE,
|
|
hidden_states: torch.Tensor,
|
|
forward_batch: Optional[ForwardBatch] = None,
|
|
input_ids_global: Optional[torch.Tensor] = None,
|
|
) -> torch.Tensor:
|
|
num_tokens = hidden_states.shape[0]
|
|
|
|
sbo_overlap_flag = (
|
|
moe.alt_stream is not None
|
|
and moe.num_fused_shared_experts == 0
|
|
and num_tokens > 0
|
|
and get_is_capture_mode()
|
|
)
|
|
|
|
if sbo_overlap_flag:
|
|
current_stream = torch.cuda.current_stream()
|
|
moe.alt_stream.wait_stream(current_stream)
|
|
shared_output = moe._forward_shared_experts(hidden_states)
|
|
mega_stream_ctx = torch.cuda.stream(moe.alt_stream)
|
|
else:
|
|
shared_output = moe._forward_shared_experts(hidden_states)
|
|
mega_stream_ctx = nullcontext()
|
|
|
|
with mega_stream_ctx:
|
|
y = _run_mega_routed(
|
|
moe, hidden_states, forward_batch, input_ids_global, num_tokens
|
|
)
|
|
|
|
if sbo_overlap_flag:
|
|
current_stream.wait_stream(moe.alt_stream)
|
|
|
|
if shared_output is not None:
|
|
y.add_(shared_output)
|
|
return y
|
|
|
|
|
|
def _run_mega_routed(
|
|
moe: DeepseekV2MoE,
|
|
hidden_states: torch.Tensor,
|
|
forward_batch: Optional[ForwardBatch],
|
|
input_ids_global: Optional[torch.Tensor],
|
|
num_tokens: int,
|
|
) -> torch.Tensor:
|
|
import deep_gemm
|
|
|
|
from sglang.srt.distributed.parallel_state import get_moe_ep_group
|
|
|
|
hidden_size = moe.config.hidden_size
|
|
|
|
if num_tokens > 0:
|
|
router_logits = moe.gate(hidden_states, forward_batch=forward_batch)
|
|
topk_kwargs = {"input_ids": input_ids_global} if moe.is_hash else {}
|
|
topk_output = moe.topk(
|
|
hidden_states,
|
|
router_logits,
|
|
num_token_non_padded=(
|
|
forward_batch.num_token_non_padded
|
|
if forward_batch is not None
|
|
else None
|
|
),
|
|
expert_location_dispatch_info=ExpertLocationDispatchInfo.init_new(
|
|
layer_id=moe.layer_id,
|
|
),
|
|
**topk_kwargs,
|
|
)
|
|
topk_ids = topk_output.topk_ids
|
|
topk_weights = topk_output.topk_weights
|
|
else:
|
|
topk_ids = None
|
|
topk_weights = None
|
|
|
|
ep_group = get_moe_ep_group().device_group
|
|
num_experts = moe.experts.num_experts
|
|
top_k = moe.config.num_experts_per_tok + moe.num_fused_shared_experts
|
|
intermediate_size = moe.config.moe_intermediate_size
|
|
num_max_tokens_per_rank = (
|
|
envs.SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK.get()
|
|
)
|
|
assert num_tokens <= num_max_tokens_per_rank, (
|
|
f"mega MoE: num_tokens={num_tokens} exceeds cap "
|
|
f"SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK="
|
|
f"{num_max_tokens_per_rank}; raise the env var or shrink "
|
|
f"cuda_graph_max_bs / chunked_prefill_size accordingly"
|
|
)
|
|
|
|
buf = _get_mega_moe_symm_buffer(
|
|
ep_group,
|
|
num_experts=num_experts,
|
|
num_max_tokens_per_rank=num_max_tokens_per_rank,
|
|
num_topk=top_k,
|
|
hidden=hidden_size,
|
|
intermediate_hidden=intermediate_size,
|
|
)
|
|
|
|
if num_tokens > 0:
|
|
topk_ids_in = topk_ids.to(torch.int32)
|
|
topk_weights_in = topk_weights.to(torch.float32)
|
|
else:
|
|
topk_ids_in = hidden_states.new_empty((0, top_k), dtype=torch.int32)
|
|
topk_weights_in = hidden_states.new_empty((0, top_k), dtype=torch.float32)
|
|
|
|
use_fp4_acts = envs.SGLANG_OPT_DEEPGEMM_MEGA_MOE_USE_FP4_ACTS.get()
|
|
if use_fp4_acts:
|
|
# FP4 path goes through DeepGEMM's mega_moe_pre_dispatch which
|
|
# handles the E2M1 packing variant. The jit implementation
|
|
# only emits FP8.
|
|
deep_gemm.mega_moe_pre_dispatch(
|
|
hidden_states,
|
|
topk_ids_in,
|
|
topk_weights_in,
|
|
buf.x,
|
|
buf.x_sf,
|
|
buf.topk_idx,
|
|
buf.topk_weights,
|
|
num_tokens=num_tokens,
|
|
group_size=32,
|
|
use_fp4_acts=True,
|
|
)
|
|
else:
|
|
mega_moe_pre_dispatch(
|
|
hidden_states,
|
|
topk_ids_in,
|
|
topk_weights_in,
|
|
buf.x,
|
|
buf.x_sf,
|
|
buf.topk_idx,
|
|
buf.topk_weights,
|
|
quant_group_size=32,
|
|
)
|
|
|
|
# Allocate at least one row so y has a non-null CUDA data_ptr;
|
|
# the DeepGEMM tvm-ffi binding rejects nullptr in convert_to_torch_tensor().
|
|
y = torch.empty(
|
|
(max(num_tokens, 1), hidden_size),
|
|
dtype=torch.bfloat16,
|
|
device=hidden_states.device,
|
|
)
|
|
swiglu_limit = getattr(moe.config, "swiglu_limit", None)
|
|
deep_gemm.fp8_fp4_mega_moe(
|
|
y,
|
|
moe.experts.mega_l1_weights,
|
|
moe.experts.mega_l2_weights,
|
|
buf,
|
|
recipe=(1, 1, 32),
|
|
activation="swiglu",
|
|
activation_clamp=swiglu_limit,
|
|
fast_math=True,
|
|
)
|
|
y = y[:num_tokens]
|
|
|
|
if not moe.experts.should_fuse_routed_scaling_factor_in_topk:
|
|
y.mul_(moe.routed_scaling_factor)
|
|
return y
|
|
|
|
|
|
def _interleave_mega_moe_gate_up(t: torch.Tensor, gran: int = 8) -> torch.Tensor:
|
|
# Match DeepGEMM's L1 gate/up layout:
|
|
# [gate: 0..7, up: 0..7, gate: 8..15, up: 8..15, ...].
|
|
num_groups, n, *rest = t.shape
|
|
half = n // 2
|
|
gate = t[:, :half].reshape(num_groups, half // gran, gran, *rest)
|
|
up = t[:, half:].reshape(num_groups, half // gran, gran, *rest)
|
|
result = torch.stack([gate, up], dim=2).reshape(num_groups, n, *rest)
|
|
return torch.empty_like(t).copy_(result)
|
|
|
|
|
|
def _interleave_mega_moe_l1_weights(
|
|
l1_weights: tuple[torch.Tensor, torch.Tensor],
|
|
) -> tuple[torch.Tensor, torch.Tensor]:
|
|
return (
|
|
_interleave_mega_moe_gate_up(l1_weights[0]),
|
|
_interleave_mega_moe_gate_up(l1_weights[1]),
|
|
)
|
|
|
|
|
|
def _transpose_mega_moe_sf_for_utccp(sf: torch.Tensor) -> torch.Tensor:
|
|
num_groups, mn, packed_sf_k = sf.shape
|
|
assert sf.dtype == torch.int and mn % 128 == 0
|
|
result = (
|
|
sf.reshape(num_groups, -1, 4, 32, packed_sf_k)
|
|
.transpose(2, 3)
|
|
.reshape(num_groups, mn, packed_sf_k)
|
|
)
|
|
return torch.empty_like(sf).copy_(result)
|
|
|
|
|
|
def build_mega_moe_experts_weights(experts) -> None:
|
|
from deep_gemm import (
|
|
transform_sf_into_required_layout,
|
|
transform_weights_for_mega_moe,
|
|
)
|
|
|
|
if getattr(experts, "_mega_moe_weights_built", False):
|
|
return
|
|
|
|
w13 = experts.w13_weight.data
|
|
w13_sf_fp32 = experts.w13_weight_scale_inv.data
|
|
w2 = experts.w2_weight.data
|
|
w2_sf_fp32 = experts.w2_weight_scale_inv.data
|
|
|
|
num_groups, n1, half_k1 = w13.shape
|
|
k1 = half_k1 * 2
|
|
_, n2, half_k2 = w2.shape
|
|
k2 = half_k2 * 2
|
|
|
|
w13_sf = transform_sf_into_required_layout(
|
|
w13_sf_fp32,
|
|
mn=n1,
|
|
k=k1,
|
|
recipe=(1, 32),
|
|
num_groups=num_groups,
|
|
disable_ue8m0_cast=False,
|
|
)
|
|
w2_sf = transform_sf_into_required_layout(
|
|
w2_sf_fp32,
|
|
mn=n2,
|
|
k=k2,
|
|
recipe=(1, 32),
|
|
num_groups=num_groups,
|
|
disable_ue8m0_cast=False,
|
|
)
|
|
|
|
if envs.SGLANG_OPT_FIX_MEGA_MOE_MEMORY.get():
|
|
# Build the interleaved L1 weight + scale once; share the weight buffer
|
|
# between `w13_weight.data` (normal deep-ep path) and `mega_l1_weights[0]`
|
|
# (mega moe path). Mega moe additionally needs a UTCCP-transposed scale;
|
|
# the deep-ep path consumes the non-transposed interleaved scale and a
|
|
# swizzle-aware activation kernel. L2 weight is untouched by the mega
|
|
# transform, so the existing `w2_weight.data` is shared directly.
|
|
w13_interleaved, w13_sf_interleaved = _interleave_mega_moe_l1_weights(
|
|
(w13, w13_sf)
|
|
)
|
|
w13_sf_utccp = _transpose_mega_moe_sf_for_utccp(w13_sf_interleaved)
|
|
w2_sf_utccp = _transpose_mega_moe_sf_for_utccp(w2_sf)
|
|
|
|
experts.w13_weight.data = w13_interleaved
|
|
experts.w13_weight_scale_inv.data = w13_sf_interleaved
|
|
experts.w2_weight_scale_inv.data = w2_sf
|
|
experts.w13_weight_scale_inv.format_ue8m0 = True
|
|
experts.w2_weight_scale_inv.format_ue8m0 = True
|
|
|
|
experts.mega_l1_weights = (experts.w13_weight.data, w13_sf_utccp)
|
|
experts.mega_l2_weights = (experts.w2_weight.data, w2_sf_utccp)
|
|
else:
|
|
l1_pair, l2_pair = transform_weights_for_mega_moe((w13, w13_sf), (w2, w2_sf))
|
|
|
|
experts.mega_l1_weights = l1_pair
|
|
experts.mega_l2_weights = l2_pair
|
|
|
|
experts._mega_moe_weights_built = True
|