Prhub

#48763 [Perf] Fix moe `reduce_scatter` perf regression by removing additional comm, 5% E2E throughput gain back.

原始 PR 作者 yewentao256 合并时间 2026-07-26 00:36 文件变更 2 提交数 11 评论 8 代码增减 +13 / -37

执行摘要

移除 MTP 额外通信,恢复 5% E2E 吞吐

作为 Issue #46654(GLM 5.2 Performance Optimization)的一部分,继 PR #48036 之后的后续优化,同时作为 PR #48657 的替代方案。该 PR 主要解决 MoE reduce_scatter 导致的性能回归,移除冗余的 all_gather 通信,恢复模型吞吐。

值得精读,设计决策:将 all_gather 从独立的恢复函数移到 forward 中条件执行,通信时机更明确;deepseek_v32 中区分 SP/non-SP 路径并合理调整通信顺序,避免了额外 all_gather。可作为性能调优的参考案例。

讨论亮点
  • deepseek_v32 中错误使用标量缩放:reviewer tlrmchlsmth 指出在 vllm/models/deepseek_v32/nvidia/mtp.py 的 forward 中,原代码使用了 hidden_states = hidden_states * get_tensor_model_parallel_world_size() 来替代 all_reduce,这在数学上不等价。作者确认后改为 tensor_model_parallel_all_reduce
  • deepseek_mtp 中冗余条件检查:reviewer 指出在 vllm/model_executor/models/deepseek_mtp.py 中,use_sequence_parallel_moe 与 shape 检查存在冗余,因为当 SP 未启用时 shape 必然匹配。作者接受并简化了条件。

实现拆解

  1. 移除辅助函数 _restore_full_token_layout_if_needed:在 vllm/model_executor/models/deepseek_mtp.py 中,删除该函数,该函数负责在 SP 场景下通过 cat 和 split 恢复完整 token 布局。

  2. 内联 all_gather 到 forward:在 DeepSeekMultiTokenPredictorLayer.forward 中,residual 相加后直接检查 use_sequence_parallel_moe,若开启则执行 tensor_model_parallel_all_gather 并截取,避免了原函数中的 cat/split 开销。

  3. 调整 deepseek_v32 的通信顺序:在 vllm/models/deepseek_v32/nvidia/mtp.py 中,同样移除对 _restore_full_token_layout_if_needed 的调用;将 non-SP 下的 all_reduce 提前,SP 下的 all_gather 推迟到共享 head 归一化之后,确保数值正确并减少通信量。

  4. 移除冗余条件检查:根据 reviewer 建议,简化 deepseek_mtp.py 中的条件判断,去掉了当 SP 未启用时不必要的 shape 检查。

  5. 基准验证:作者在 PR body 中提供了 GLM-5.2 模型的 serving benchmark 和 lm_eval gsm8k 结果,表明吞吐提升约 5% 且精度无损。

文件 模块 状态 重要度
vllm/model_executor/models/deepseek_mtp.py MTP 预测 modified 7.14
vllm/models/deepseek_v32/nvidia/mtp.py MTP 预测 modified 6.55

关键符号

DeepSeekMultiTokenPredictorLayer.forward DeepseekV32MultiTokenPredictorLayer.forward

关键源码片段

vllm/model_executor/models/deepseek_mtp.py core-logic

核心通用 MTP 模块,移除了辅助函数并简化 forward 通信逻辑

# DeepSeekMultiTokenPredictorLayer.forward 的简化片段
hidden_states, residual = self.mtp_block(
    positions=positions,
    hidden_states=hidden_states,
    residual=None,
)
hidden_states = residual + hidden_states # pre-final-norm (logits hidden)
# 如果开启了序列并行 (SP),则需要将分散在各 TP rank 上的 token 收集完整
if self.mtp_block.use_sequence_parallel_moe:
    hidden_states = tensor_model_parallel_all_gather(hidden_states, 0)
    hidden_states = hidden_states[: positions.shape[0]]
# shared_head 内部执行 RMSNorm,返回 norm 后的结果供下一 draft step 使用
return hidden_states, self.shared_head(hidden_states)

评论区精华

deepseek_v32 中错误使用标量缩放替代 all_reduce 正确性

tlrmchlsmth 指出在 deepseek_v32 的 forward 中不应用标量乘 world_size,而应使用 all_reduce,否则导致数值错误。

结论:改为使用 tensor_model_parallel_all_reduce,修复正确性问题。 · 已解决

deepseek_mtp 中冗余条件检查 style

tlrmchlsmth 指出两个条件检查(use_sequence_parallel_moe 和 shape 检查)冗余,因为非 SP 时 shape 必然匹配。

结论:接受,移除多余检查,简化代码。 · 已解决

风险与影响

  • 回归风险:改动集中在 deepseek 模型 MTP 模块,虽然涉及 forward 核心路径,但 benchmark 已验证正确性,风险可控。
  • 数值等价性风险:通信操作的顺序调整(all_gather 移到 norm 之后)可能影响浮点结果,但 all_gather 是线性操作,不改变数值语义。
  • 测试覆盖风险:无新增测试文件,依赖现有集成测试和 benchmark,若后续引入非标场景可能覆盖不足。
  • 用户影响:使用 DeepSeek 模型(特别是 GLM-5.2)并启用 MTP 和序列并行的用户将获得约 5% 的吞吐量提升。
  • 系统影响:减少了序列并行情况下的通信量,从 all_gather(2H) + all_reduce(H) 降为 all_gather(H),降低网络压力。
  • 开发者影响:代码更简洁,移除了单独的辅助函数,降低了维护成本。
通信模式变更 数值等价性风险 缺少测试覆盖

关联 Issue

#46654 [Feature]: GLM 5.2 Performance Optimization

完整报告

参与讨论