Prhub

#24906 Support Qwen3.5 NVFP4 MTP DeepEP

原始 PR 作者 YAMY1234 合并时间 2026-05-15 10:49 文件变更 4 提交数 4 评论 14 代码增减 +58 / -5

执行摘要

支持 Qwen3.5 NVFP4 MTP 与 DeepEP 低延迟模式

Qwen3.5 NVFP4 disaggregated serving with DeepEP and NEXTN/MTP currently fails in the MTP draft path on GB300/SM100+. Three issues:

1) MTP draft MoE can have quant_config=None;
2) DeepEP low-latency dispatch needs kNumMaxTopK >= 10;
3) FlashInfer GDN target verify is not supported on SM100+. This PR adds Python-side fixes for issues 1 and 3.

此 PR 涉及 DeepEP 低延迟模式与 MTP 的集成,以及 GPU 架构感知的 verify 内核选择,设计取舍值得关注。建议负责 DeepEP 和推测解码的工程师精读,特别是 forward_unquantized_deepep_ll 的 fallback 实现和 bf16_dispatch 的配置传播。

讨论亮点

gemini-code-assist[bot] 建议优化 forward_unquantized_deepep_ll 中的掩码计算,预计算 valid_mask 以减少冗余 unsqueeze 和取反操作(性能/风格建议)。
ispobock 询问是否可直接使用 flashinfer_kernel.supports_target_verify 属性替代复杂条件,作者回复已解决。
ch-wan 指出 ep_moe/layer.py 中无条件设置 bf16_dispatch 可能过于宽泛,影响其他配置;作者承认并计划参考 PR#17392 改进。ch-wan 曾建议回退以避免阻塞 PR#22822,但后来 PR#22822 已合并。
ch-wanYAMY1234 讨论了回退此 PR 以加速 PR#22822 的可能性,作者表示可接受并计划后续重新引入。

实现拆解

  1. 处理未量化 draft MoE 层:在 ep_moe/layer.py__init__ 中,当 quant_config is None 时通过 dispatcher.set_quant_config({"bf16_dispatch": True}) 强制 BF16 分发;在 run_moe_core 中增加 quant_config is None 分支以避免调用 quant_config.get_name();新增 forward_unquantized_deepep_ll 方法执行 w13 -> silu(gate)*up -> w2 并屏蔽 padding 行。
  2. 支持显式 BF16 分发配置:在 token_dispatcher/deepep.py_dispatch_core 中读取 bf16_dispatch 键并优先于其他条件判断,确保 draft 层以 BF16 格式分发隐藏状态。
  3. 限定 FlashInfer GDN verify 支持范围:在 kernels/gdn_flashinfer.py 中添加 supports_target_verify 属性,基于 SM major 决定(SM90 支持,SM100+ 不支持)。
  4. 更新 verify kernel 选择逻辑:在 gdn_backend.py 中增加 flashinfer_kernel.supports_target_verify 检查,仅在确认支持时使用 FlashInfer 验证内核,否则回退到 Triton。
  5. 验证:通过 Qwen3.5 NVFP4 GPQA 端到端测试确认正确性,但未添加新的单元测试。
文件 模块 状态 重要度
python/sglang/srt/layers/moe/ep_moe/layer.py MoE 层 modified 7.24
python/sglang/srt/layers/attention/linear/gdn_backend.py 注意力层 modified 5.47
python/sglang/srt/layers/moe/token_dispatcher/deepep.py 分发器 modified 5.21
python/sglang/srt/layers/attention/linear/kernels/gdn_flashinfer.py 注意力层 modified 4.59

关键符号

forward_unquantized_deepep_ll GDNBackend.__init__ _dispatch_core FlashInferGDNKernel.__init__

关键源码片段

python/sglang/srt/layers/moe/ep_moe/layer.py core-logic

核心改动:处理 quant_config=None,新增 forward_unquantized_deepep_ll 方法,实现未量化 DeepEP 低延迟 MoE 前向。

# Inside run_moe_core - handling DeepEP low-latency dispatch output
elif DispatchOutputChecker.format_is_deepep_ll(dispatch_output):
    if self.quant_config is None:
        # MTP draft MoE layer without quant config: use BF16 fallback
        output = self.forward_unquantized_deepep_ll(dispatch_output)
    elif (
        get_moe_runner_backend().is_flashinfer_cutedsl()
        and self.quant_config is not None
        and self.quant_config.get_name() == "modelopt_fp4"
    ):
        output = self.forward_flashinfer_cutedsl(dispatch_output)
    else:
        # fallback to other forward paths
        ...def forward_unquantized_deepep_ll(
    self,
    dispatch_output: DeepEPLLDispatchOutput,
):
    # BF16 fallback for DeepEP low-latency mode when no quant config is present
    hidden_states, hidden_states_scale, _, _, masked_m, _ = dispatch_output
    assert hidden_states_scale is None
    assert self.moe_runner_config.activation == "silu"
    assert self.moe_runner_config.is_gated
    assert hidden_states.dim() == 3
​
    num_experts, max_tokens, _ = hidden_states.shape
    token_offsets = torch.arange(max_tokens, device=hidden_states.device)
    # Create mask for valid tokens per expert (ignore padding rows)
    valid_mask = (
        token_offsets.unsqueeze(0) < masked_m[:num_experts].unsqueeze(1)
    ).unsqueeze(-1)
    hidden_states = hidden_states.masked_fill(~valid_mask, 0)
​
    # Gate-up projection: (E, T, dim) -> (E, T, 2 * inter_dim)
    gate_up = torch.bmm(hidden_states, self.w13_weight.transpose(1, 2))
    w13_bias = getattr(self, "w13_weight_bias", None)
    if w13_bias is not None:
        gate_up += w13_bias.unsqueeze(1)
​
    gate, up = gate_up.chunk(2, dim=-1)
    hidden_states = F.silu(gate) * up
​
    # Down projection
    output = torch.bmm(hidden_states, self.w2_weight.transpose(1, 2))
    w2_bias = getattr(self, "w2_weight_bias", None)
    if w2_bias is not None:
        output += w2_bias.unsqueeze(1)
​
    # Zero out padding rows in output
    return output.masked_fill(~valid_mask, 0)
python/sglang/srt/layers/attention/linear/gdn_backend.py core-logic

修改 verify kernel 选择逻辑,添加 supports_target_verify 检查以在 SM100+ 上回退到 Triton。

# Verify kernel: use FlashInfer only when the selected FlashInfer kernel
# supports MTP verify. On SM100+ FlashInfer GDN decode is supported, but
# its MTP verify path is not, so keep Triton as the verify fallback.
if (
    decode_backend.is_flashinfer() or prefill_backend.is_flashinfer()
) and flashinfer_kernel.supports_target_verify:
    self.verify_kernel = flashinfer_kernel
else:
    self.verify_kernel = triton_kernel

评论区精华

优化 forward_unquantized_deepep_ll 中的掩码计算 性能

gemini-code-assist[bot] 建议预计算 valid_mask 的维度以减少冗余的 unsqueeze 和取反操作,提高可读性和轻微优化。

结论:建议未被合并者采纳或拒绝,但代码中保留了原始实现。 · commented

简化 GDN verify kernel 的选择条件 question

ispobock 询问是否可以直接使用 flashinfer_kernel.supports_target_verify 属性,而不需要 decode_backend 和 prefill_backend 组合判断。

结论:YAMY1234 回复 "Resolved!",最终使用该属性作为判断依据,但保留了 decode/prefill 的 OR 条件。 · 已解决

bf16_dispatch 设置过于宽泛的设计担忧 设计

ch-wan 指出无条件设置 bf16_dispatch 可能不正确,影响其他配置。YAMY1234 承认问题并计划参考 PR#17392 改进,ch-wan 曾建议回退以避免阻塞 PR#22822。

结论:PR#22822 合并后该担忧部分缓解,代码中保留了 bf16_dispatch 设置,但作者计划后续优化。 · 已解决

回退 PR 以加速 PR#22822 合并的讨论 other

ch-wan 提议回退此 PR 的 bf16_dispatch 部分以不阻塞 PR#22822,YAMY1234 表示可以接受并愿意后续重新引入。

结论:由于 PR#22822 在讨论结束后不久合并,回退未实际执行。 · 已解决

风险与影响

  1. 性能风险:新增的 forward_unquantized_deepep_ll 是纯 Python 手写 BF16 前向,未使用 Triton 或 CUTLASS 优化,在高吞吐场景下可能成为瓶颈。
  2. 配置传播风险bf16_dispatch 的全局设置可能无意中影响其他量化配置的路径,虽然当前 quant_config is None 条件已限制范围,但仍可能干扰未来新增的 unquantized 场景。
  3. 测试覆盖不足:本 PR 仅依赖 GPQA 端到端验证,缺少针对 forward_unquantized_deepep_llsupports_target_verify 的单元测试。
  4. 兼容性风险:GDN verify 回退到 Triton 可能在某些边缘 case 下产生数值差异,但 FlashInfer 和 Triton 应该在数学上等价。

仅影响 --speculative-algorithm NEXTN--speculative-moe-runner-backend flashinfer_cutedsl 的 NVFP4 量化模型(如 Qwen3.5-397B-A17B-NVFP4)在 GB300/SM100+ 上的 MTP draft 路径。对其他模型、量化配置、GPU 架构无影响。系统吞吐经 GPQA 验证为 296.8 token/s。

未量化 MoE fallback 路径 过于宽泛的 BF16 dispatch 设置 缺少测试覆盖

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论