[Kimi-K3] Use explicit SiTU activation for MegaMoE (#34883)

This commit is contained in:
Baizhou Zhang
2026-08-15 01:41:59 -07:00
committed by GitHub
parent c87a2ced12
commit 7769f54feb
4 changed files with 68 additions and 24 deletions
+1 -1
View File
@@ -69,7 +69,7 @@ dependencies = [
"sentencepiece", "sentencepiece",
"setproctitle", "setproctitle",
"sgl-deep-ep==0.1.0", "sgl-deep-ep==0.1.0",
"sgl-deep-gemm==0.1.5.post2", "sgl-deep-gemm==0.1.5.post3",
"sglang-kernel==0.4.6.post1", "sglang-kernel==0.4.6.post1",
"smg-grpc-servicer>=0.5.0", "smg-grpc-servicer>=0.5.0",
"soundfile==0.13.1", "soundfile==0.13.1",
@@ -342,6 +342,10 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
# Weights stay MXFP4 (e2m1 + ue8m0 g32, zero requantization); # Weights stay MXFP4 (e2m1 + ue8m0 g32, zero requantization);
# activations are quantized to fp8 per-token-group-128. # activations are quantized to fp8 per-token-group-128.
self.use_deep_gemm = get_moe_runner_backend().is_deep_gemm() self.use_deep_gemm = get_moe_runner_backend().is_deep_gemm()
# MegaMoE calls DeepGEMM directly instead of going through the selected
# MoE runner, so it needs the same unpadded checkpoint layout and
# DeepGEMM scale preparation even when the runner is flashinfer_mxfp4.
self.use_mega_moe = get_moe_a2a_backend().is_megamoe()
self.flashinfer_mxfp4_moe_precision = ( self.flashinfer_mxfp4_moe_precision = (
get_exec().moe.flashinfer_mxfp4_moe_precision get_exec().moe.flashinfer_mxfp4_moe_precision
) )
@@ -392,7 +396,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
# pad the intermediate size to be a multiple of 2 * mxfp4_block # pad the intermediate size to be a multiple of 2 * mxfp4_block
# for to hold non-uniform sharded tensor as well as swizzling # for to hold non-uniform sharded tensor as well as swizzling
intermediate_size_per_partition_after_pad = intermediate_size_per_partition intermediate_size_per_partition_after_pad = intermediate_size_per_partition
if self.use_marlin: if self.use_marlin and not self.use_mega_moe:
intermediate_size_per_partition_after_pad = round_up( intermediate_size_per_partition_after_pad = round_up(
intermediate_size_per_partition, 128 intermediate_size_per_partition, 128
) )
@@ -402,7 +406,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
intermediate_size_per_partition_after_pad intermediate_size_per_partition_after_pad
- layer.intermediate_size_per_partition - layer.intermediate_size_per_partition
) )
elif self.use_deep_gemm: elif self.use_deep_gemm or self.use_mega_moe:
# DeepGEMM fp8_fp4 grouped GEMM consumes the checkpoint layout # DeepGEMM fp8_fp4 grouped GEMM consumes the checkpoint layout
# directly (packed e2m1 K-major + ue8m0 g32 scales); no padding. # directly (packed e2m1 K-major + ue8m0 g32 scales); no padding.
pass pass
@@ -544,7 +548,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
set_weight_attrs(w2_weight_bias, extra_weight_attrs) set_weight_attrs(w2_weight_bias, extra_weight_attrs)
def process_weights_after_loading(self, layer): def process_weights_after_loading(self, layer):
if self.use_marlin: if self.use_marlin and not self.use_mega_moe:
from sglang.srt.layers.quantization.marlin_utils import ( from sglang.srt.layers.quantization.marlin_utils import (
check_moe_marlin_supports_layer, check_moe_marlin_supports_layer,
) )
@@ -572,7 +576,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
layer._mxfp4_backend = "marlin" layer._mxfp4_backend = "marlin"
return return
if self.use_deep_gemm: if self.use_deep_gemm or self.use_mega_moe:
from deep_gemm import transform_sf_into_required_layout from deep_gemm import transform_sf_into_required_layout
# Packed fp4 (e2m1 x2 per byte) weights: DeepGEMM expects int8. # Packed fp4 (e2m1 x2 per byte) weights: DeepGEMM expects int8.
@@ -597,7 +601,7 @@ class Mxfp4MoEMethod(FusedMoEMethodBase):
num_groups=num_experts, num_groups=num_experts,
disable_ue8m0_cast=False, disable_ue8m0_cast=False,
) )
if get_moe_a2a_backend().is_megamoe(): if self.use_mega_moe:
# MegaMoE consumes the same transformed sf, plus its own # MegaMoE consumes the same transformed sf, plus its own
# interleaved/UTCCP weight layout. K3 routes EVERY batch # interleaved/UTCCP weight layout. K3 routes EVERY batch
# through mega (the megamoe backend has no a2a fallback), so # through mega (the megamoe backend has no a2a fallback), so
+4 -16
View File
@@ -141,14 +141,6 @@ def _cdiv(a: int, b: int) -> int:
return (a + b - 1) // b return (a + b - 1) // b
# MegaMoE SiTU sentinel: DeepGEMM 0.1.5.post1+ selects the K3 SiTU
# activation when activation_clamp == 0.03125 (2^-5: exactly representable and
# unused by any legitimate swiglu clamp; the host asserts clamp >= 0 so a
# negative sentinel is impossible). beta=4.0 / linear_beta=25.0 are baked into
# the DeepGEMM kernel.
_K3_MEGA_SITU_SENTINEL_CLAMP = 0.03125
def _k3_bf16_gemm( def _k3_bf16_gemm(
x: torch.Tensor, x: torch.Tensor,
weight: torch.Tensor, weight: torch.Tensor,
@@ -487,9 +479,8 @@ class KimiK3MoE(nn.Module):
# through it when enabled — the megamoe backend's non-mega fallback is # through it when enabled — the megamoe backend's non-mega fallback is
# a StandardDispatcher without a2a, which is wrong for scattered # a StandardDispatcher without a2a, which is wrong for scattered
# tokens — so SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK must # tokens — so SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK must
# cover the per-rank prefill chunk. SiTU is selected inside the # cover the per-rank prefill chunk. The DeepGEMM MegaMoE SiTU kernel
# DeepGEMM mega kernel via a sentinel activation_clamp with the K3 # bakes in the K3 activation constants.
# constants baked in.
self._use_mega_moe = get_moe_a2a_backend().is_megamoe() self._use_mega_moe = get_moe_a2a_backend().is_megamoe()
self._mega_intermediate_size = moe_intermediate_size self._mega_intermediate_size = moe_intermediate_size
self._mega_top_k = config.num_experts_per_token self._mega_top_k = config.num_experts_per_token
@@ -499,7 +490,7 @@ class KimiK3MoE(nn.Module):
config.activation_situ_beta, config.activation_situ_beta,
config.activation_situ_linear_beta, config.activation_situ_linear_beta,
) == (4.0, 25.0), ( ) == (4.0, 25.0), (
"mega SiTU kernel patch bakes beta=4.0/linear_beta=25.0; " "MegaMoE SiTU kernel bakes beta=4.0/linear_beta=25.0; "
"got a checkpoint with different constants" "got a checkpoint with different constants"
) )
@@ -802,10 +793,7 @@ class KimiK3MoE(nn.Module):
self.experts.mega_l2_weights, self.experts.mega_l2_weights,
buf, buf,
recipe=(1, 1, 32), recipe=(1, 1, 32),
activation="swiglu", activation="situ",
# Sentinel: selects the K3 SiTU branch in the DeepGEMM mega kernel
# (beta=4.0 / linear_beta=25.0 baked in).
activation_clamp=_K3_MEGA_SITU_SENTINEL_CLAMP,
fast_math=True, fast_math=True,
) )
y = y[:num_tokens] y = y[:num_tokens]
@@ -1,6 +1,6 @@
"""B300 per-commit CI coverage for Kimi-K3 serving recipes. """B300 per-commit CI coverage for Kimi-K3 serving recipes.
Runs the Low Latency DSPARK recipe and the Balanced DCP/HiCache recipe on Runs the Low Latency DSPARK, Balanced DCP/HiCache, and MegaMoE recipes on
eight B300 GPUs. Each server must preserve basic model quality on GSM8K, and eight B300 GPUs. Each server must preserve basic model quality on GSM8K, and
the Low Latency recipe must also preserve single-request decode performance. the Low Latency recipe must also preserve single-request decode performance.
""" """
@@ -18,13 +18,17 @@ from sglang.test.test_utils import (
popen_launch_server, popen_launch_server,
) )
register_cuda_ci(est_time=900, stage="base-c", runner_config="8-gpu-b300") register_cuda_ci(est_time=1200, stage="base-c", runner_config="8-gpu-b300")
MODEL_PATH = ( MODEL_PATH = (
"/data/radixark/model-cache/hub/models--moonshotai--Kimi-K3/" "/data/radixark/model-cache/hub/models--moonshotai--Kimi-K3/"
"snapshots/9f62e4e9fffbd0a83ddd60e1c209d828994b3569" "snapshots/9f62e4e9fffbd0a83ddd60e1c209d828994b3569"
) )
DSPARK_DRAFT_MODEL = "RadixArk/Kimi-K3-DSpark" DSPARK_DRAFT_MODEL = "RadixArk/Kimi-K3-DSpark"
MEGAMOE_URL = "http://0.0.0.0:30000"
MEGAMOE_ENV = {
"SGLANG_OPT_DEEPGEMM_MEGA_MOE_NUM_MAX_TOKENS_PER_RANK": "8320",
}
SERVER_LAUNCH_TIMEOUT = 3600 SERVER_LAUNCH_TIMEOUT = 3600
GPU_IDLE_TIMEOUT = 120 GPU_IDLE_TIMEOUT = 120
@@ -124,5 +128,53 @@ class TestKimiK3B300Balanced(GSM8KMixin, CustomTestCase):
_stop_server(getattr(cls, "process", None)) _stop_server(getattr(cls, "process", None))
class TestKimiK3B300MegaMoE(GSM8KMixin, CustomTestCase):
"""TP8/EP8/DCP8 MegaMoE recipe with DSPARK speculation."""
gsm8k_score_threshold = 0.95
gsm8k_num_examples = 200
@classmethod
def setUpClass(cls):
cls.model = MODEL_PATH
cls.base_url = MEGAMOE_URL
cls.process = popen_launch_server(
cls.model,
cls.base_url,
timeout=SERVER_LAUNCH_TIMEOUT,
other_args=[
"--trust-remote-code",
"--tp-size",
"8",
"--moe-a2a-backend",
"megamoe",
"--ep",
"8",
"--dcp-size",
"8",
"--mem-fraction-static",
"0.85",
"--reasoning-parser",
"kimi_k3",
"--tool-call-parser",
"kimi_k3",
"--mamba-full-memory-ratio",
"5.13",
"--speculative-algorithm",
"DSPARK",
"--speculative-draft-model-path",
DSPARK_DRAFT_MODEL,
"--speculative-dspark-block-size",
"7",
"--enable-linear-replayssm-spec",
],
env=MEGAMOE_ENV,
)
@classmethod
def tearDownClass(cls):
_stop_server(getattr(cls, "process", None))
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()