执行摘要
- 一句话:DSpark 在 DP attention 下支持 MegaMoE 后端
- 推荐动作:值得精读,但重点不在代码量,而在于它展示了一个典型的「先论证 lockstep 前提、再放开参数限制」的工程范式:用
_fill_dp_moe_sync_metadata 的 all-gather 与 run_idle_participation 的参与机制证明 EP 下每 rank MoE 调用次数一致,再以 SGLANG_RAGGED_VERIFY_MODE=static 兜底图 tier 一致性。PR body 附带的正确性/性能双验证也值得作为后续同类 gate 变更的参考模板。
功能与动机
PR body 明确指出:_handle_dspark 在 --enable-dp-attention 开启时拒绝了所有非 none 的 --moe-a2a-backend,导致 DSpark 无法与 EP all-to-all 分发(如 MegaMoE)同时使用。作者论证该限制已过时:DSV4-MoE draft 路径已在 _fill_dp_moe_sync_metadata 中 all-gather global_num_tokens,且无本地生成请求的 rank 仍通过 run_idle_participation 进入 draft forward,因此每个 EP rank 每步调用 MoE 的次数一致,MegaMoE 的 fused kernel 已能容纳零 token rank,lockstep 前提已满足。
实现拆解
实现共两步:
- 放宽
_handle_dspark 的 a2a 后端校验(python/sglang/srt/arg_groups/speculative_hook.py):
- 将原来的
moe_a2a_backend != "none" 拒绝条件改为 not in ("none", "megamoe"),即除内置 TP MoE 外,只放行 megamoe,deepep、pplx 等其他后端仍按原名报错。
- 新增第二道校验:当
moe_a2a_backend != "none" 时,读取 SGLANG_RAGGED_VERIFY_MODE 环境变量,非 static 模式直接 raise,原因是 per-rank 图 tier 只有在 _dp_tier_gather_enabled(依赖 require_mlp_tp_gather,随 a2a 后端配置变化)成立时才能达成一致,token 维度 tier 不一致会导致各 rank 重放不同图形状。import 采用函数内延迟导入,避免模块级循环依赖。
- 新增门禁契约测试(
test/registered/spec/dspark/test_dspark_draft_path_default.py):
- 新增
TestDsparkDpAttentionMoeA2aGate 测试类,通过 _dp_server_args 构造 --enable-dp-attention --enable-dp-lm-head dp_size=2 tp_size=2 的参数组合。
test_only_megamoe_is_admitted 在 static 模式下验证 megamoe 通过、deepep/pplx 按名抛出 ValueError。
test_a2a_backend_with_compact_verify_mode_raises 验证 compact 模式下即使 megamoe 也会被拒绝。
- 新增
from sglang.srt.environ import envs 导入,用于测试中覆盖环境变量。
- CI 与测试配套:无新增配置文件,测试注册为 CPU CI(
register_cpu_ci),无需 GPU 即可跑通参数校验逻辑;PR 附带了 8x GB300 双节点 TP8/DP8/EP8 的速度与 profile 数据。
关键文件:
python/sglang/srt/arg_groups/speculative_hook.py(模块 参数校验;类别 source;类型 dependency-wiring;符号 _handle_dspark): 核心变更文件:_handle_dspark 的 a2a 后端白名单从仅 none 扩展为 none/megamoe,并新增 SGLANG_RAGGED_VERIFY_MODE=static 的强制校验,是 DSpark + DP attention + EP 组合能否启动的 gate 所在。
test/registered/spec/dspark/test_dspark_draft_path_default.py(模块 测试;类别 test;类型 test-coverage;符号 TestDsparkDpAttentionMoeA2aGate, _dp_server_args, test_only_megamoe_is_admitted, test_a2a_backend_with_compact_verify_mode_raises): 新增 TestDsparkDpAttentionMoeA2aGate 测试类,覆盖白名单两侧(megamoe 通过 / deepep、pplx 拒绝)与 compact 模式拒绝,是本次 gate 变更的回归保障。
关键符号:_handle_dspark
关键源码片段
python/sglang/srt/arg_groups/speculative_hook.py
核心变更文件:_handle_dspark 的 a2a 后端白名单从仅 none 扩展为 none/megamoe,并新增 SGLANG_RAGGED_VERIFY_MODE=static 的强制校验,是 DSpark + DP attention + EP 组合能否启动的 gate 所在。
# python/sglang/srt/arg_groups/speculative_hook.py
# DSpark + dp attention 下允许的 MoE a2a 后端白名单
def _handle_dspark(server_args: ServerArgs) -> None:
_is_npu = server_args.device.startswith("npu")
if not server_args.device.startswith("cuda") and not _is_npu:
raise ValueError("DSpark speculative decoding only supports CUDA and NPU devices.")
# dp_size == 1 且开 dp_attention 是 DSV4 CP 下的退化组合,跳过 DP 专属检查
if server_args.enable_dp_attention and server_args.dp_size > 1:
if not server_args.enable_dp_lm_head:
raise ValueError("DSpark with dp attention requires --enable-dp-lm-head.")
# 白名单:内置 TP MoE(none)或 MegaMoE;其余后端保持拒绝并点名
if not _is_npu and server_args.moe_a2a_backend not in ("none", "megamoe"):
raise ValueError(
"DSpark with dp attention supports moe_a2a_backend 'none' "
"(built-in TP MoE) or 'megamoe', got "
f"{server_args.moe_a2a_backend!r}."
)
# MegaMoE 等 a2a 后端依赖 per-rank 图 tier 一致:
# _dp_tier_gather_enabled 依赖 require_mlp_tp_gather(随 a2a 配置变化),
# 若 rank 间 token 维度 tier 不一致会重放不同图形状,因此强制 static 模式。
# 函数内延迟 import,避免模块级循环依赖。
if not _is_npu and server_args.moe_a2a_backend != "none":
from sglang.srt.speculative.ragged_verify import (
RaggedVerifyMode,
read_ragged_verify_mode,
)
if read_ragged_verify_mode() is not RaggedVerifyMode.STATIC:
raise ValueError(
"DSpark with dp attention + "
f"moe_a2a_backend={server_args.moe_a2a_backend!r} requires "
"SGLANG_RAGGED_VERIFY_MODE=static."
)
if server_args.attn_cp_size > 1:
raise ValueError(
"DSpark with dp attention does not support context parallel "
f"(attn_cp_size={server_args.attn_cp_size})."
)
# spec 后端必须与目标后端一致,否则 EP 集合通信会错位
if (
not _is_npu
and server_args.speculative_moe_a2a_backend is not None
and server_args.speculative_moe_a2a_backend != server_args.moe_a2a_backend
):
raise ValueError(
"DSpark ignores --speculative-moe-a2a-backend; with dp attention it "
f"must match the target moe_a2a_backend={server_args.moe_a2a_backend!r} "
f"(got {server_args.speculative_moe_a2a_backend!r})."
)
test/registered/spec/dspark/test_dspark_draft_path_default.py
新增 TestDsparkDpAttentionMoeA2aGate 测试类,覆盖白名单两侧(megamoe 通过 / deepep、pplx 拒绝)与 compact 模式拒绝,是本次 gate 变更的回归保障。
# test/registered/spec/dspark/test_dspark_draft_path_default.py
# DSpark + dp attention + MoE a2a 后端的门禁契约测试
class TestDsparkDpAttentionMoeA2aGate(CustomTestCase):
"""Gate contract for DSpark + dp attention + MoE a2a backends."""
def _dp_server_args(self, *, moe_a2a_backend: str) -> ServerArgs:
# 构造 DSpark + DP attention 的典型参数组合
server_args = _make_dspark_server_args(
model_path=_BUNDLED_MODEL_PATH, hf_config=_bundled_hf_config()
)
server_args.enable_dp_attention = True
server_args.enable_dp_lm_head = True
server_args.dp_size = 2
server_args.tp_size = 2
server_args.moe_a2a_backend = moe_a2a_backend
return server_args
def test_only_megamoe_is_admitted(self):
"""Both sides of the allowlist: megamoe passes, others raise by name."""
# static 模式下 megamoe 应被放行,deepep/pplx 按名抛错
with envs.SGLANG_RAGGED_VERIFY_MODE.override("static"):
_handle_dspark(self._dp_server_args(moe_a2a_backend="megamoe"))
for backend in ("deepep", "pplx"):
with self.assertRaisesRegex(ValueError, backend):
_handle_dspark(self._dp_server_args(moe_a2a_backend=backend))
def test_a2a_backend_with_compact_verify_mode_raises(self):
# compact 模式无法保证 per-rank 图 tier 一致,megamoe 也必须拒绝
server_args = self._dp_server_args(moe_a2a_backend="megamoe")
with envs.SGLANG_RAGGED_VERIFY_MODE.override("compact"):
with self.assertRaisesRegex(ValueError, "static"):
_handle_dspark(server_args)
评论区精华
PR 无 review 评论,唯一 Discussion 是作者触发的 /tag-and-rerun-ci(恰好对应 Extra CI 失败后的重跑操作),reviewer hnyls2002 直接 APPROVED。PR body 中作者以数据自证了关键设计判断:gsm8k 400 题两臂均为 0.9775,8x GB300 下 640/640 请求成功、无 CUDA/NCCL/barrier 错误,并指出 target-verify 与 draft-verify CUDA graph 在 8 个 EP rank 上完全一致,以排除 phase-flip barrier 反同步风险。
- CI extra 运行失败与重跑 (other): 通过重跑命令重试,reviewer
hnyls2002 最终 APPROVED。
风险与影响
- 风险:
- 图形状一致性风险:
SGLANG_RAGGED_VERIFY_MODE=static 是硬性前置条件,若用户遗漏该环境变量,_handle_dspark 直接报错,属预期防护;但若运行环境与参数校验时的环境不一致(如 worker 进程环境被改动),理论上仍可能出现 rank 间图 tier 分歧。
- 依赖面扩大:
megamoe 路径涉及的 fused kernel 与 DeepEP 类后端不同,当前仅在 moe_a2a_backend != "none" 分支内做校验,后续若新增其他兼容后端,需同步扩展 _dp_tier_gather_enabled 判定。
- 覆盖缺口:测试只覆盖参数门禁,未覆盖真实 CUDA 图验证;PR body 的 8 卡数据是一次性验证,仓库 CI 不保证该组合持续回归。
- 兼容性:NPU 设备沿用
_is_npu 分支跳过 a2a 校验,行为不变,但 megamoe 在 NPU 上的表现未验证。
- 影响:影响范围集中在 DSpark + DP attention 的部署组合:此前必须退回内置 TP MoE,现在可选用 MegaMoE(EP)获得更优的 MoE 扩展性。对现有使用 none 后端的用户零影响;对其他 a2a 后端用户,报错信息更明确(按名提示)。团队侧新增了一个参数组合的门禁契约测试,后续改动 _handle_dspark 时会被该测试约束。整体影响面小且正向。
- 风险标记:参数门禁变更, 环境变量强依赖, 缺少端到端回归, 仅限 CUDA 验证
关联脉络
- PR #34816 [Perf] Publish the WAR read-done event at DSPARK verify: 同为 DSpark verify 阶段的调度与同步优化,关注 DSPARK verify 路径的 rank 间同步事件。
- PR #33006 fix(dsa): use FlashInfer fused top-k for packed PAGED rows: 同为 DeepSeek 系 MoE 路径的后端选型与 fallback 移除,涉及 DSA 下 MoE 相关 kernel 选择。
- PR #34810 fix(qwen3): support DeepEP-class backends and early EPLB state: 同属 MoE a2a 类后端兼容性修复,关注 DeepEP/EP 后端与调度器的配合。
- PR #34019 [SM12x] Default the fused MHC post+pre path on: 同为 DeepSeek 系在 DP/EP 配置下的性能路径默认化变更,关注跨 rank 图同步。
参与讨论