/label add triaged
👋 您好,欢迎向 MindStudio-Modeling 提交 Issue!
我们已收到您的反馈,感谢你对开源社区的支持。🎉
📅处理时效: 维护团队将在8小时内 查看并回复您的问题(工作日)。
🔍自助查询: 在等待期间,建议您先查阅以下资料,可能已有解决方案:
📖 MindStudio-Modeling官方文档
📝 贡献者指南
请确保 Issue 描述清晰,包含复现步骤和日志,这将帮助我们更快定位问题。谢谢!


此说的是:DeepSeek V3.2 这类标准门控 MoE,在打开 --enable-shared-expert-tp 时,共享专家的 TP 是合法的,但 routed 专家的分发量被多算了一倍。仿真还能跑完,时延和吞吐却错了。

问题分析:
每个 token 选 8 个专家,所以「要分发的对数」= token 数 × 8。
TP=2 时,每个 rank 只应处理一半 token:512 / 2 = 256。
本 rank 正确分发量 = 256 × 8 = 2048。
若误用全量 512:512 × 8 = 4096。
init_routing_v2 有两个输入:
hidden:本 rank 的 token,形状 [token 数, 7168]
top-k 路由表:[token 数, 8]
关开关时两边都是 256,对得上,后面 all_to_all 就是 [2048, 7168]。
开开关(有 bug)时:
hidden 已经是 256(TP 切开了)
路由表还是 512×8(没切)
分发按 topk.numel() = 4096 走,于是 all_to_all 变成 [4096, 7168],时延约 2 倍
unpermute、专家 GEMM(16→32)跟着放大
最后输出对不齐:1835008 vs 3670016(正好 2 倍),代码把多出来的截掉,形状看起来又对了
MLA / indexer 两边都是 [512, ...],没有翻倍——说明不是「整层都算大了」,而是 只有跟路由 token 数挂钩的分发链路错了。
修改方案
在 ParallelMoELayer.forward 中,route_after_dp_transform=False:先 route(),再 _dp_transform_enter。若 topk 的 token 维仍大于切开后的 hidden,对 indices/weights 做与 hidden 同一套 pad + 按 rank 切开,再进 fused_moe。只在对不齐时切,避免 raw-logits / 自定义 router / 先切再 route 被切两次。

修改影响
无 CLI/API 变化。默认不开 --enable-shared-expert-tp 所有模型都不变。
会变(且仅当开该 flag,且 TP>1、DP≠EP)
标准门控、无自定义 gate_router、默认先 route 再切:
DeepSeek V3.2(旗舰场景)
Qwen3-MoE / Qwen3.5-MoE / Qwen3-Next / Qwen3-VL
Ling、Ernie 4.5 MoE、Minimax M2、MiMo-v2-Flash
这些模型开开关时,routed 分发从 N×k 回到 (N/TP)×k(本次复现 4096→2048),时延/TPS 更接近关开关,只多共享专家那次 all-reduce。是修正高估,不是算子变快。
不变
DeepSeek V3、GLM4-MoE、GLM4V、GLM5:raw-logits,route() 里已切
DeepSeek V4、Minimax M3、Bailing V3:自定义 gate_router
Kimi K3 / K2.5:route_after_dp_transform=True


/label add resolved


您好,当前Issue标记为resolved且有一段时间未进一步更新,因此我们将其标记为'stale'(闲置)状态。若您认为这是误操作,可通过添加任意评论来去除'stale'标签。标记为stale的Issue在4天内无更新活动将自动关闭。


[Medium][模型适配]
--enable-shared-expert-tp时标准门控模型的 topk 未随 hidden_states 做 TP 切分,MoE 分发/通信量被高估 tp_size 倍run_id:
repro-moe-topk-mismatch-20260831Problem
ParallelMoELayer.forward的enable_shared_expert_tp分支(tensor_cast/layers/moe_layer.py,route_after_dp_transform=False默认路径)中,route()在_dp_transform_enter()之前对全量 token[N, H]计算路由:gate_returns_raw_logits=True,如 deepseek_v3)在route()内部把 router_logits 切到本 rank 分片,其注释明确该切片用于对齐_dp_transform_enter()随后对 hidden_states 的 TP 切片;(topk_indices, topk_weights)tuple 或裸 logits,即gate_returns_raw_logits=False且无gate_router,如 deepseek_v32)不做任何切片。随后
_dp_transform_enter()把 hidden_states 切到[N/tp, H],而 topk_indices 仍为[N, k]。FusedMoETensorCast.forward以num_tokens = topk_indices.numel()(=N*k,应为N*k/tp)驱动全部下游尺寸(init_routing_v2输出行数、all_to_all 分发量、每专家 GEMM M 维、出口 all_gather/unpermute token 量),全部膨胀 tp_size 倍。末尾截断逻辑使最终输出形状恰好正确——无崩溃、无告警,纯静默的性能高估。术语:N = 本 DP rank 打包后总 token 数;tp = 全局 tensor parallel size;k(top_k)= 每 token 路由的专家数(DeepSeek-V3.2 为 8)。
Reproduction(纯官方 CLI,两条命令,无任何脚本/探针)
设计说明
本 bug 由官方 CLI 公开参数组合自然触发,且异常直接体现在 CLI 自带的 analytic metrics 表(stdout)中,无需任何辅助工具。触发组与对照组仅差一个 flag,其余(模型、N、tp、ep、层数)完全一致。模型使用仓库自带 config-only 资产
tests/assets/model_config/deepseek_v32,全程离线。复现步骤
# Step 1: 触发组(bug 分支) uv run python -m cli.inference.text_generate ` tests/assets/model_config/deepseek_v32 ` --num-queries 4 --query-length 128 --device TEST_DEVICE ` --num-devices 2 --tp-size 2 --ep-size 2 ` --enable-shared-expert-tp ` --num-hidden-layers-override 4 --no-repetition # Step 2: 对照组(同命令,仅去掉 --enable-shared-expert-tp) uv run python -m cli.inference.text_generate ` tests/assets/model_config/deepseek_v32 ` --num-queries 4 --query-length 128 --device TEST_DEVICE ` --num-devices 2 --tp-size 2 --ep-size 2 ` --num-hidden-layers-override 4 --no-repetitionStep 1 预期:MoE 分发链路(all_to_all / unpermute_tokens / init_routing_v2)时延与对照组一致(该 flag 只影响共享专家的 TP 化,不改变 routed expert 的分发量,见下方数值推理)。
Step 1 实际:三项全部约 2× 于对照组(all_to_all 2.999ms vs 1.501ms),总时延 +12.7%。
Step 2 预期/实际:正常完成,作为健康基线。
判定:必现(确定性;两组均 exit=0、无告警——静默)。
Test Results(stock CLI stdout,commit 7a07630)
从两组 metrics 表摘取关键行(完整表见各自 stdout):
MoE 分发链路(时延 ∝ 路由 token 数):
tensor_cast.all_to_alltensor_cast.unpermute_tokenstensor_cast.init_routing_v2与路由 token 数无关的算子(权重/激活带宽型,应不变):
tensor_cast.mla_sparse_attentiontensor_cast.dsa_indexertensor_cast.mlapo_quanttensor_cast.static_quant_linear汇总:
all_reduce数值推理(正确值 2048、实际 4096 的推导)
全部输入来自 CLI 参数与模型 config,不需要观测任何内部张量:
num_experts_per_tok= 8;--num-devices 2 --tp-size 2→ world=2,dp = 2/(tp×pp) = 1。_dp_transform_enter()即做此切片,出口 all_gather 重组)。因此每 rank 应分发的 (token, expert) 对 = 256 × 8 = 2048。Expected
--enable-shared-expert-tp开启时,标准门控模型每 rank 的 MoE 分发量应为(N/tp)×k对(本例 2048):触发组的 all_to_all / unpermute_tokens / init_routing_v2 应与对照组一致(约 1.5ms / 58μs / 33μs),总时延回到约 19.8ms 量级。Actual
每 rank 分发量为
N×k对(本例 4096,= tp_size × 正确值),MoE 分发/通信量被高估 tp_size 倍:总时延 +12.7%,吞吐被低估 11.3%(tp=2 实测;tp 更大时线性放大)。输出形状靠截断修正,无任何告警。Root Cause
tensor_cast/layers/moe_layer.py三处叠加:# ParallelMoELayer.forward — enable_shared_expert_tp 分支,route_after_dp_transform=False(默认) topk_indices, topk_weights = self._inner.route( hidden_states, ... # hidden_states: [N, H] 全量(切片前) ) # topk_indices: [N, k] ← 标准门控分支不做 TP 切片 hidden_states, num_tokens = self._dp_transform_enter(hidden_states) # hidden_states: [N/tp, H](global_tp_group.slice) # FusedMoETensorCast.forward num_tokens = topk_indices.numel() # = N*k,应为 N*k/tp(仅由 topk 尺寸决定)MoELayer.route内部仅 raw_logits 分支(deepseek_v3 等模型)内置pad + tensor_split(tp_size)[tp_rank]切片,注释明确该切片用于对齐_dp_transform_enter()的 hidden_states 切片;标准门控分支(gate 返回 tuple / 裸 logits)无对应逻辑。同仓正确对照(修复时可参考):
route_after_dp_transform=True(kimi_k3/kimi_k25 设置):在_dp_transform_enter()之后路由,天然一致;enable_shared_expert_tp=False路径:MoELayer.forward内部先 enter 后 route,一致,且有回归测试断言(test_parallel_moe_ep_route_before_tp_slice_smoke:fused_moe 入参((3,16),(3,2))即 hidden 与 topk 的 token 数必须一致)。测试盲区:
tests/regression/tensor_cast/test_parallel_moe.py的两个 shared_expert_tp 测试均使用_make_fake_gate(shard_by_tp=True)(假门控自行模拟 raw_logits 式 TP 切片),标准门控模型在该分支上零覆盖。Fix 建议
在
route_after_dp_transform=False路径中,对route()返回的topk_indices/topk_weights执行与 raw_logits 分支相同的pad + tensor_split(tp_size)[tp_rank]切片(或统一在route()内置切片,使两个门控分支行为一致)。验收标准(即上述两条 CLI 命令):触发组的 all_to_all / unpermute_tokens / init_routing_v2 与对照组一致,总时延回到对照组量级;并补一条标准门控(shard_by_tp=False)的 shared_expert_tp 回归测试。Environment
tests/assets/model_config/deepseek_v32(仓库自带 config-only 资产,离线)Impact / Severity
gate_returns_raw_logits=False且无gate_router的 MoE 模型(deepseek_v32、qwen3_moe、ling、ernie4_5_moe、minimax_m2 等)在--ep-size >1 --tp-size >1 --enable-shared-expert-tp下。DeepSeek-V3.2 是该特性的旗舰用户(共享专家 + EP 部署)。--enable-shared-expert-tp(但该 flag 正是 shared-expert TP 部署形态的建模入口,规避即丢失目标场景)。相似 Issue 检索
已检索现有 issue 关键字(shared_expert_tp / topk / MoE / DP transform),未发现重复。
SIG 路由
tensor_cast/layers/→ 模型适配 SIG(chairChenHuiwen,reviewersstormchasingg/jhon-117/weixin_43113933,approverlutean)。