mindspore-ai / mindspore-ai/hyper-parallel
SAPP-ND 实测验证发现的问题(P1-P4)
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 53
- Forks
- 63
- Avg merge
- 23h 45m
- Merged PRs (30d)
- 63
Description
SAPP-ND 实测验证发现的问题(P1-P4)
PR #827(Issue #132)在 DeepSeek-V3 缩小版上做了 6 config 的 profiling 实测验证,发现了 4 个问题。其中 P2(EP/ZeRO-3 抵消)和 P4 的 EP_WAIT 单位问题是 EP 估算模块本身的 bug,P1(TP 运行时开销未建模)和 P3(PP 通信估算)是其他模块的问题,但在 EP 验证实验中一起暴露出来。
1. 背景
实验模型为 DeepSeek-V3 缩小版(~1.3B, 32 experts, 12 层, PP=2, 32 卡),覆盖 EP(2/4/8) × TP(1/2) 共 6 个配置。
Peak HBM 预估 vs 实测:
| Config | EP | TP | DP | 实测 Peak(MB) | 预估 Peak(MB) | 预估/实测 |
|---|---|---|---|---|---|---|
| A | 2 | 1 | 16 | 10941 | 8255 | 0.75 (低估) |
| B | 4 | 1 | 16 | 10056 | 7382 | 0.73 (低估) |
| C | 8 | 1 | 16 | 9614 | 6945 | 0.72 (低估) |
| D | 4 | 2 | 8 | 5850 | 6174 | 1.06 |
| E | 2 | 2 | 8 | 6735 | 7047 | 1.05 |
| F | 8 | 2 | 8 | 5408 | 5737 | 1.06 |
通信分量 Pearson r:
| Wait Type | Pearson r | 状态 |
|---|---|---|
| COMP | 0.999 | OK |
| MP_WAIT | 0.890 | OK |
| EP_WAIT | 0.872 | PASS |
| PP_WAIT | -0.197 | NEGATIVE (P3) |
| TOTAL | -0.534 | NEGATIVE (P4) |
从上述数据中可以清楚看到两类问题:显存预估的 TP/EP 偏差,以及通信估算的排序反转。按优先级整理如下。
2. 已知问题
2.1 P1:TP 依赖的基线偏差(OOM 风险)
现象
TP=1 组系统性低估约 25%(0.72-0.75x),TP=2 组略高估约 5%(1.05-1.06x)。绝对偏差:TP=1 平均低估 ~2676 MB,TP=2 平均略高估 ~322 MB。
影响
TP=1 低估 25% 超过了 20% 的可接受阈值。更危险的是,这是 OOM 方向的错误:策略搜索认为某配置可以部署,实际运行却 OOM。
根因
TP=1 时有约 2676 MB 的运行时开销没有被模型计入,而 TP=2 时这些开销几乎消失——因为它们会被隐式分片。来源:
- 输出层 activation ~2084 MB 未被 recompute 覆盖
- 通信 double-buffering ~500-600 MB
- Graph memory、框架开销
- 安全余量仅 1 GB,远不足以覆盖上述开销
实测 TP1/TP2 = 1.63-1.78x,而预估只有 1.17-1.21x,说明模型严重低估了 TP 带来的实际节省。
关键点:上述运行时开销(activation buffer、通信 buffer、graph memory)是 TP-shardable 的——TP>1 时它们随 TP 线性缩小,但当前模型中的 Static 分量(param/OS/grad)已通过 shard_p_os_non_exp_partial 等因子正确缩放,Dynamic 分量(activation + comm)也通过 _inner_dynamic_mem 按 TP 缩放。问题在于这些额外的运行时开销项完全不在模型中,而 1 GB 的 safety buffer 既不够大也不随 TP 缩放。
相关代码
_backbone.py:660-662— safety buffer 硬编码为 1 GB(flat,不随 TP 缩放),仅加到 Dynamic>0 的 stage_backbone.py:651-658— Dynamic = sum(activation + comm) + backward_overhead + safety_bufferbody.py:62— non-expert param 使用shard_p_os_non_exp_partial,当 has_op=True 时为os_max_shard * cp(非简单 /t),当 has_op=False 时为t * cpbody.py:88— non-expert grad 使用shard_grad_non_exp,当 has_grad_shard=False 时为t(纯 TP 分片),当 has_grad_shard=True 时为shard_p_os_non_exp
2.2 P2:EP 分片与 ZeRO-3 分片抵消
现象
6 个配置中,expert param 始终为 304 MB、OS 始终为 608 MB,EP 从 2 增到 8 完全没有变化。而实测中 expert 参数只做 EP 分片(不做 ZeRO-3 分片),应随 EP 线性缩放。
影响
这是一个 per-stage 的 bug。在当前 DeepSeek-V3 缩小版中,Stage 1 的 EP-constant 组件(Param+OS+Safety Buffer)占比约 34%,稀释了 EP-scalable 部分,所以 EP scaling 看起来很弱(1.09x~1.14x),抵消的影响也被稀释了。但对于 expert 占比更大的模型,策略搜索可能会因此选错 EP 值。
根因
stat_p_layer 中 expert param 公式:
routed_mem = routed_p / ep * bytes_p / shard_p_os_exp
shard_p_os_exp = d_exp * cp * t_exp(when has_op=True),而 d_exp 在所有分支中都包含 1/ep:
| d_exp 分支 | 公式 | 代入后 shard_p_os_exp |
|---|---|---|
| etp 已设置 (line 131) | d * t * cp / t_exp / ep |
d * t * cp² / ep |
| etp 未设置, d ≥ ep (line 135) | d / ep |
d / ep * cp * t_exp |
| etp 未设置, d < ep (line 137) | d * t / ep |
d * t / ep * cp * t_exp |
以 etp 分支为例,代入后分子的 /ep 和分母中的 1/ep 刚好抵消:routed_mem = routed_p * bytes_p / (d * t * cp²),EP 完全消失。其他分支同理,EP 均被抵消。
同样的抵消影响 OS、Grad 和 Grad-clip(body.py:130)。
为什么不能直接去掉 d_exp 中的 1/ep
有些 MoE 模型确实对 expert 参数做 ZeRO-3 分片,此时 d_exp 包含 1/ep 是正确的——抵消反映的是真实行为。DeepSeek-V3 不对 expert 参数做 ZeRO-3 分片,抵消就是 bug。所以不能一刀切,需要配置项来区分这两种场景。
相关代码
_cost_model_parser.py:131,135,137—config_dp_tp_exp()计算d_exp(三个分支均含/ep)_cost_model_parser.py:59-61—config_optimizer_shard()计算shard_p_os_exp_cost_model_parser.py:62-66—shard_grad_exp(当 has_grad_shard 时等于 shard_p_os_exp)body.py:58—stat_p_layer中 routed_mem 公式body.py:72—stat_os_layer中 routed_mem 公式body.py:84—stat_grad_layer中 routed_mem 公式body.py:130— gradient clipping buffer 也使用routed_p / ccfg.ep * ccfg.bytes_os / ccfg.shard_p_os_exp,同样受消去影响。注意完整公式为(routed_p / ep * bytes_os / shard_p_os_exp + shared_p * bytes_os / shard_p_os_exp_partial) * bytes_os / shard_p_os_non_exp_partial * int(has_clip),其中外层还乘以bytes_os / shard_p_os_non_exp_partial,但内层routed_p / ep * bytes_os / shard_p_os_exp的 EP 消去行为与 param/OS/grad 一致_cost_model_variables.py:88-116—_CostModVardataclass 中目前无expert_zero3_sharding字段
2.3 P3:PP_WAIT 负相关
现象
PP_WAIT 的 Pearson r = -0.197,呈负相关——模型认为 PP_WAIT 更高的配置,实测反而更低,排序完全反转。
影响
这个问题不影响显存估算,但会影响性能估算——策略搜索可能选错 PP 配置。同时 PP_WAIT 的负相关也拖累了 TOTAL 估算(见 P4)。
根因
当前 PP_WAIT = BUBBLE + PP_COMM(debug.py:590-593),其中:
- BUBBLE =
pipeline_perf - time_sum(estimate.py:325-332),time_sum包含所有 PerfParts(含 EP_COMM) - PP_COMM 使用硬编码的
MANUAL_P2P_RATIO = 0.002(estimate.py:42),与 EP 无关
⚠️ 数学推导表明原根因方向错误:对 vp=1 的 1F1B schedule(DeepSeek-V3 测试配置),形式化推导:
pipeline_perf = sum(s_i) + (m-1) * max(s_i) # estimate.py:255
time_sum = m * max(s_i) # estimate.py:315-317 sanity check
BUBBLE = pipeline_perf - time_sum
= sum(s_i) - max(s_i) # = min(s_i) for p=2
对 p=2, 添加 EP_COMM Δ 到各 stage 的情况分析:
| 场景 | EP_COMM 分布 | BUBBLE 变化 |
|---|---|---|
| 等量增加 | Δ 加到两个 stage | BUBBLE' = min(s₁,s₂) + Δ → 增大 Δ |
| 仅加到 straggler | Δ 只加到 max(s₁,s₂) | BUBBLE' = min(s₁,s₂) → 不变 |
| 仅加到非 straggler | Δ 只加到 min(s₁,s₂) | BUBBLE' = min(s₁,s₂) + Δ → 增大 Δ(若不切换 straggler) |
结论:BUBBLE 随 EP_COMM 增大而增大或不变,绝不会减小。原分析"EP_COMM 填入 time_sum 导致 BUBBLE 减小"在数学上不成立。
DeepSeek-V3 的特殊结构:arch_hooks.py:218-231 中 hook_dense 设置 ccfg.ep = 1(零 EP_COMM),hook_moe 设置 ccfg.ep = saved.ep(完整 EP_COMM)。因此 EP_COMM 在 pipeline stage 间不均匀——dense 层所在的 stage 不受 EP_COMM 影响,MoE 层所在的 stage 承受全部 EP_COMM。这使得 BUBBLE 的变化取决于哪个 stage 是 straggler,但方向仍为"增大或不变"。
真正的根因是估算模型与现实的系统性偏差:估算模型将 EP_COMM 视为串行工作,增加 stage 执行时间 → BUBBLE 增大。而实际运行中 EP all-to-all 可以与 pipeline bubble 重叠(stage 等待其他 stage 时仍可执行 EP 通信),EP_COMM 不增加实际 stage 时间 → PP_WAIT 随 EP 减小。估算的 BUBBLE 增大方向与实测的 PP_WAIT 减小方向矛盾,根因是估算缺少 pipeline-EP overlap 建模。
可能的次级因素(需通过消融实验验证):
- DP 减少效应:EP 增大 → DP 减小(总卡数固定)→ DP 通信量减少 → stage 执行更快。但当前
comm[Dim.DP] = 0(comm_time.py:397的 TO REMOVE hack)已将 DP 通信清零,此机制在估算中被掩盖 - Regression 系数失真:
apply_regression_coefficients()对各 PerfParts 独立缩放,EP_COMM 和 COMPUTE 的系数不同可能导致 BUBBLE 被扭曲 - Straggler 切换:EP 变化可能导致两个 stage 的相对执行时间反转,但如上表分析,即使切换 straggler,BUBBLE 也不会减小
此外,当前 pipeline schedule 仅区分 vp==1(1F1B)和 vp>1(interleaved),没有考虑 zero_bubble_v 等 schedule 的不同 bubble 比例。
相关代码
estimate.py:238-333—estimate_pipeline()计算 BUBBLEestimate.py:295-313— time_sum 累加循环(含 EP_COMM)estimate.py:336-369—estimate_p2p_comm()计算 PP_COMMestimate.py:467-499—apply_regression_coefficients()独立缩放各 PerfPartsdebug.py:587-593—estimation_in_real_parts()中 EP_WAIT = EP_COMM、PP_WAIT = BUBBLE + PP_COMM 的映射arch_hooks.py:218-231—hook_dense设置ccfg.ep=1、hook_moe设置ccfg.ep=saved.ep,导致 EP_COMM 在 stage 间不均匀comm_time.py:397—comm[Dim.DP] = 0(TO REMOVE hack,将 DP 通信清零)pipeline_builder.py:25-177— schedule 构建器(1f1b / vpp / vpp2)common.py:382-389— zero_bubble_v schedule 检测
2.4 P4:TOTAL 负相关与 EP_WAIT 绝对值单位问题
现象
TOTAL 的 Pearson r = -0.534,整体负相关。另外 EP_WAIT 的绝对值与实测差了约 1e7 倍,是一个单位问题。
影响
总时间估算不可靠,目前只有分项(COMP、EP_WAIT)有一定参考价值。
根因
TOTAL 负相关由两个独立问题叠加导致:
- PP_WAIT 负相关(P3) 直接把 TOTAL 排序带偏
- EP_WAIT 绝对值单位错误:EP 通信公式(
comm.py:167-226)输出 byte volume(字节数),而非通信时间。在comm_time.py:366-374中,estimate_comm_score()的转换循环仅覆盖[Dim.DP, Dim.TP, Dim.DP]——EP 和 CP 从未被传入转换,无论 ttype 是否为 TIME。因此 EP_WAIT 始终为字节数,量级偏大 ~1e7 倍,淹没 COMP、DP_WAIT 等正确分量。
注意:即使 ttype == PerformanceType.TIME,EP 也不被转换。问题不是"仅在 TIME 模式下转换",而是"EP 从未被转换"。
相关代码
comm.py:167-181—ep_comm_layer_balanced()返回字节数,docstring 明确标注 "Result is in bytes"comm.py:184-226—ep_comm_layer_imbalanced()同样返回字节数comm_time.py:366-374— 关键:estimate_comm_score()转换循环为[Dim.DP, Dim.TP, Dim.DP],不含Dim.EP或Dim.CPcomm_time.py:489-507—estimate_comm_score()将字节除以各网络层级带宽得到时间comm_time.py:396-397—comm[Dim.TP] /= 2和comm[Dim.DP] = 0(TO REMOVE hack)estimate.py:492-499—TOTAL = COMP + DP_WAIT + MP_WAIT + EP_WAIT + CP_WAIT + PP_WAIT的累加debug.py:587-589—EP_WAIT = EP_COMM的直接映射
额外发现:CP 通信(cp_comm_layer)返回 element count,也从未被传入 estimate_comm_score() 转换。本期不处理 CP 单位问题,但需记录为 follow-up。
3. 目标与非目标
3.1 目标
- 修正 TP=1 时显存低估问题,预估/实测比提升至 0.85-1.10(P1)
- 新增
expert_zero3_sharding配置项,使 expert param/OS/grad/grad-clip 在不做 ZeRO-3 分片时随 EP 正确缩放(P2) - 修正 PP_WAIT 排序反转,使 Pearson r > 0.5(P3)
- 统一 EP 通信的单位,使 EP_WAIT 输出时间(秒)而非字节数,TOTAL Pearson r > 0.5(P4)
3.2 非目标
- 不引入新的显存估算模型(如基于实测的回归模型),仅修正现有公式的参数和逻辑
- 不修改 TP 通信估算逻辑(TP comm 当前返回字节数,与 EP 相同的问题,但 TP comm 的量级影响较小,本期不处理)
- 不修改 pipeline schedule 构建器本身(1f1b / vpp / vpp2 的调度逻辑不变),仅修正 bubble 估算公式
- 不修改 fast-tuner 中的 pipeline conductor 或 OR-Tools 优化器
- 不处理 CP 通信的单位问题(CP comm 返回 element count 且从未被 estimate_comm_score 转换,本期不涉及,标记为 follow-up)
expert_zero3_sharding不影响 shared expert 的分片逻辑(shared expert 不参与 EP 分片,始终走shard_p_os_exp_partial路径)
4. 接口与契约
4.1 P1:TP 运行时开销建模
4.1.1 Safety buffer 参数化
当前:_backbone.py:660-662 中 safety_buffer = 1024 * 1024 * 1024(1 GB 硬编码,flat 不随 TP 缩放)
变更:将 safety_buffer 改为从 CostModelConfig 读取,支持用户配置,并随 TP 缩放:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
safety_buffer_mb |
float | 1024 | Safety buffer 大小(MB),默认 1 GB(保持向后兼容) |
计算公式:
effective_safety_buffer = safety_buffer_mb / t (单位 MB)
注意:_backbone.py 中 Dynamic 分量以字节累加(safety_buffer = 1024 * 1024 * 1024 直接加到字节级的 Dynamic),最终通过 self.mb()(÷ 1024²)转回 MB 输出。参数 safety_buffer_mb 以 MB 为单位输入,需在内部乘以 1024² 转为字节后再加入 Dynamic,与现有字节级累加逻辑一致。
约束:
- 值 ≥ 0;0 表示不添加 safety buffer
- 仅加到 Dynamic > 0 的 stage(保持现有行为)
- 默认值 1024 MB 保持向后兼容:TP=1 时仍为 1 GB,TP=2 时为 512 MB(与原行为 1 GB 相比有变化,但 TP=2 原本已高估 5%,减小 safety buffer 可缓解高估)
设计决策:Safety buffer 代表的运行时余量(graph memory、框架开销等)本质上也是 TP-shardable 的,与 tp_shardable_overhead 一致。将其改为 /t 缩放比新增独立的 TP-shardable 开销项更简洁,避免了两个参数叠加导致 TP=2 过度高估的问题。
4.1.2 TP-shardable 运行时开销项
新增 TP-shardable 运行时开销的显式建模,覆盖 safety buffer 之外的额外开销(输出层 activation、通信 double-buffer 等),当 TP > 1 时被 /t 分片:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
tp_shardable_overhead_mb |
float | 2676 | TP-shardable 运行时开销(MB),包括输出层 activation、通信 double-buffer、graph memory 等 |
计算公式:
effective_overhead = tp_shardable_overhead_mb / t (单位 MB)
插入位置:在 _backbone.py 的 memory 汇总阶段,加到 Dynamic 分量上(与 safety_buffer 同级)。
约束:
- 值 ≥ 0;0 表示不添加额外开销项
- 对于 DeepSeek-V3 缩小版,默认值 2676 MB 为实测偏差均值
- 后续可通过 fast-tuner 校准自动设置
为什么需要两个参数:safety_buffer_mb 是通用的安全余量(所有模型都需要),tp_shardable_overhead_mb 是 MoE/特定模型结构的额外开销建模。两者职责不同,分开配置更灵活。
4.2 P2:expert_zero3_sharding 配置项
4.2.1 新增配置项
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
expert_zero3_sharding |
bool | True | expert 参数是否参与 ZeRO-3 分片。True=参与(当前行为,抵消是正确的);False=不参与(DeepSeek-V3 场景) |
添加位置:_cost_model_variables.py 的 _CostModVar dataclass,与 has_op、has_grad_shard 等布尔标志同组(line 115-116 附近)。
4.2.2 sharding factor 计算变更
当前(_cost_model_parser.py:59-61):
shard_p_os_exp = (d_exp if has_op else 1) * cp * t_exp
变更:
if expert_zero3_sharding:
shard_p_os_exp = (d_exp if has_op else 1) * cp * t_exp
else:
# expert 参数不做 ZeRO-3 分片,仅 CP+TP 分片,不含 d_exp
shard_p_os_exp = cp * t_exp
即 expert_zero3_sharding=False 时,shard_p_os_exp = cp * t_exp,不含 d_exp,EP 分片不再被 ZeRO-3 抵消。
注意:当 has_op=False 时,原公式已为 1 * cp * t_exp = cp * t_exp,此时 expert_zero3_sharding 的取值无影响——没有 ZeRO-3 就不存在抵消问题。
4.2.3 影响范围
expert_zero3_sharding=False 时受影响的公式:
| 函数 | 行号 | 公式项 | 变更 |
|---|---|---|---|
stat_p_layer |
58 | routed_p / ep * bytes_p / shard_p_os_exp |
shard_p_os_exp 不含 d_exp,分母变小,routed_mem 变大 |
stat_os_layer |
72 | routed_p / ep * 2 * bytes_os / shard_p_os_exp |
同上 |
stat_grad_layer |
84 | routed_p / ep * bytes_grad / shard_grad_exp |
shard_grad_exp = shard_p_os_exp(当 has_grad_shard),同样不含 d_exp |
| gradient clipping | 130 | routed_p / ep * bytes_os / shard_p_os_exp |
同样受消去影响 |
不受影响的公式:
shard_p_os_exp_partial(shared expert 专用,与 EP 无关)shard_p_os_non_exp_partial/shard_p_os_non_exp(non-expert 专用)shard_grad_exp_partial(shared expert gradient 专用)shard_grad_non_exp(non-expert gradient 专用)- 所有 non-expert 和 shared expert 的内存估算
4.2.4 向后兼容
- 默认值
True:现有行为完全不变,shard_p_os_exp仍含d_exp,所有现有 UT 无需修改 d_exp的计算逻辑(config_dp_tp_exp)不变——d_exp 仍按原公式计算,仅shard_p_os_exp不再使用它- 当
has_op=False时,True 和 False 的结果相同(均为cp * t_exp),不存在兼容性问题
4.3 P3:PP_WAIT 修正
4.3.1 根因确认(消融实验,优先级最高)
在实施修复前,需先通过消融实验确认负相关的真正根因:
| 实验 | 方法 | 目的 |
|---|---|---|
| EXP-1 | 将 EP_COMM 从 time_sum 中移除(同时从 pipeline_perf 中移除),观察 BUBBLE 变化方向 | 确认 overlap 缺失是否为 PP_WAIT 负相关的主因(BUBBLE 增大方向已由数学证明) |
| EXP-2 | 移除 comm[Dim.DP] = 0 hack,恢复 DP 通信,观察 BUBBLE 变化 |
验证 DP 减少效应是否被 hack 掩盖 |
| EXP-3 | 固定 EP,仅改变 DP(用不同总卡数),观察 PP_WAIT 变化 | 隔离 DP 对 PP_WAIT 的影响 |
| EXP-4 | 打印各 config 的 straggler stage id,检查是否随 EP 变化 | 验证 straggler 切换假设 |
决策规则:
- 数学推导已确认:当前 BUBBLE 随 EP 增大而增大(min(stage_perfs) 增加),与实测的 PP_WAIT 随 EP 减小方向相反。EXP-1 的目的是确认 overlap 缺失是否是主因(而非验证 BUBBLE 增大方向——这已由数学证明)。若 EXP-1 显示移除 EP_COMM 后 BUBBLE 变化仍然很小(说明 EP_COMM 对 BUBBLE 的影响被其他因素淹没),则需重新评估 §4.3.2 的优先级
- 若 EXP-2 显示 DP 通信是主因,则优先修复 DP hack(§4.3.3),P3 修法完全不同
- 若 EXP-3 或 EXP-4 显示 straggler 切换是主因,则需引入 stage-aware BUBBLE 计算
4.3.2 BUBBLE 计算中 EP 通信 overlap 修正(待 EXP-1 确认后实施)
当前(estimate.py:295-332):
time_sum 包含 EP_COMM,使得 BUBBLE = pipeline_perf - time_sum。
变更(仅当 EXP-1 确认 overlap 缺失是主因时):
引入 EP 通信与 pipeline bubble 的 overlap 修正。数学推导表明,当前 BUBBLE = min(stage_perfs)(对 vp=1, p=2),EP_COMM 作为串行工作加入 stage 后 BUBBLE 增大,导致 PP_WAIT 随 EP 增大——与实测方向相反。修正核心:让 EP 通信不增加 pipeline_perf 中的 straggler_time(对应现实中 EP 通信与 bubble 重叠),从而使 BUBBLE 随 EP 增大而减小。
# 当前:straggler_time 包含 EP_COMM(串行),pipeline_perf 随 EP 增大,BUBBLE 增大
# 变更:计算 straggler_time 时,扣除与 bubble 可重叠的 EP 通信部分
effective_straggler = straggler_time - ep_comm_per_stage * overlap_ratio
推导验证(以 p=2, overlap_ratio=1.0 为例):
修改前: pipeline_perf = sum(s_i) + (m-1)*max(s_i), BUBBLE = min(s_i) → 随 EP 增大
修改后: pipeline_perf = sum(s_i') + (m-1)*max(s_i'), s_i' = s_i - ep_comm*1.0
time_sum 不变(仍含 EP_COMM)
BUBBLE = min(s_i - ep_comm) < min(s_i) → 随 EP 增大而减小 ✓
其中 overlap_ratio 为 EP 通信与 pipeline bubble 的重叠比例:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
ep_bubble_overlap_ratio |
float | 1.0 | EP 通信与 pipeline bubble 的重叠比例。1.0=完全重叠(EP 通信在 bubble 时间内完成,不增加 straggler),0.0=无重叠(当前行为,BUBBLE 随 EP 增大) |
效果:当 overlap_ratio=1.0 时,EP 通信不增加 effective_straggler,pipeline_perf 不随 EP 增大,而 time_sum 中的 EP_COMM 仍计为有用工作,BUBBLE = pipeline_perf - time_sum 随 EP 增大而减小,PP_WAIT 减小——与实测方向一致。
约束:
- 0 ≤
ep_bubble_overlap_ratio≤ 1.0 - 当
ep ≤ 1时,ratio 不生效(无 EP 通信) - DeepSeek-V3 实测表明 overlap 接近 1.0(EP 通信几乎全在 bubble 内完成)
- 此方案需 EXP-1 确认后才能实施,否则可能引入新的偏差
4.3.3 DP 通信 hack 清理(待 EXP-2 确认后实施)
当前(comm_time.py:397):comm[Dim.DP] = 0(TO REMOVE hack,将 DP 通信清零)
变更:移除此 hack,恢复 DP 通信计算。若 EXP-2 确认 DP 通信是 PP_WAIT 负相关的主因,则此修复可能单独解决 P3。
风险:移除 DP=0 hack 后,DP 通信重新参与 TOTAL 累加,可能影响 TOTAL 的 Pearson r。需同步移除 comm[Dim.TP] /= 2 hack(line 396),并用 UT 验证。
4.3.4 Pipeline schedule bubble 比例公式(预留)
当前 pipeline schedule 仅区分 vp==1 和 vp>1,未来可扩展为按 schedule 类型使用不同 bubble 比例:
| Schedule | Bubble 比例公式 |
|---|---|
| 1F1B (vp=1) | (pp-1) / (pp + mb - 1) |
| Interleaved (vp>1) | (pp-1) / (pp*vp + mb - 1) |
| Zero-Bubble-V | 待定(需实测校准) |
本期暂不实现此变更(需同步修改 fast-tuner 的 pipeline conductor),仅预留接口。当 pipeline_scheduler 配置项可用时,bubble 比例公式按上表选择。
4.4 P4:EP 通信单位统一
4.4.1 EP 通信强制转换为时间
当前:comm.py 中 ep_comm_layer_balanced() 和 ep_comm_layer_imbalanced() 返回字节数。comm_time.py:366-374 中 estimate_comm_score() 的转换循环为 [Dim.DP, Dim.TP, Dim.DP],不含 Dim.EP——无论 ttype 是否为 TIME,EP 从未被转换。
变更:在 comm_time.py 的 estimate_from_mem_comm() 中,将 Dim.EP 加入 estimate_comm_score() 转换循环:
# 当前
for dim, ov in zip([Dim.DP, Dim.TP, Dim.DP], [0.9, 0, 0.0]):
# 变更
for dim, ov in zip([Dim.DP, Dim.TP, Dim.DP, Dim.EP], [0.9, 0, 0.0, 0.0]):
EP 通信的 overlap 默认为 0.0(无 overlap),后续可通过配置调整。
接口变更:
| 位置 | 变更 |
|---|---|
comm.py — ep_comm_layer_balanced() |
保持返回字节数(不改函数签名) |
comm.py — ep_comm_layer_imbalanced() |
保持返回字节数(不改函数签名) |
comm_time.py:366-374 — 转换循环 |
将 Dim.EP 加入循环,调用 estimate_comm_score(comm[Dim.EP], Dim.EP, overlap=0.0) 转换为时间 |
debug.py — estimation_in_real_parts() |
EP_WAIT 映射不变,但值从字节数变为秒 |
关于 ttype 条件:当前转换循环整体受 if param["ccfg"].ttype == PerformanceType.TIME: 条件保护。变更后,EP 转换仍在此条件内——仅当 ttype==TIME 时转换。这意味着当 ttype != TIME 时 EP 仍为字节数,但这与 DP/TP 的行为一致(DP/TP 也仅在 TIME 模式下转换)。若需要无条件转换,需单独讨论是否改变 ttype 的语义。
约束:
- TP 通信(
tp_comm_layer)当前也返回字节数,且已在转换循环中(Dim.TP),本期无需额外处理 - DP 通信(
dp_comm_layer)返回 element count,在转换循环中被当作字节数传入estimate_comm_score()——这是一个潜在的单位不一致,但本期不处理 - CP 通信(
cp_comm_layer)返回 element count,也从未被转换——标记为 follow-up - 需同步移除
comm_time.py:396-397的TO REMOVEhack(与 P3 §4.3.3 联动)
4.4.2 带宽参数
estimate_comm_score() 已有完整的带宽模型(comm_time.py:489-507),使用 device.level_bandwidth 和 device.level_assign() 按网络层级计算。无需新增参数,确保 EP 通信正确传入即可。
5. 测试设计
5.1 单元测试
P1 测试
| 用例 ID | 描述 | 期望 |
|---|---|---|
| P1-01 | safety_buffer_mb=1024(默认)、TP=1 时,effective_safety_buffer = 1024 MB,行为与修改前完全一致 | Dynamic 分量与修改前相同 |
| P1-02 | safety_buffer_mb=1024、TP=2 时,effective_safety_buffer = 512 MB | Dynamic 分量比 TP=1 少 512 MB(safety buffer 差异) |
| P1-03 | tp_shardable_overhead_mb=2676、TP=1 时,effective_overhead = 2676 MB,预估 Peak 增加 2676 MB | TP=1 偏差从 ~2676 MB 缩小到接近 0 |
| P1-04 | tp_shardable_overhead_mb=2676、TP=2 时,effective_overhead = 1338 MB | TP=2 预估/实测比保持在合理范围 |
| P1-05 | safety_buffer_mb=0 且 tp_shardable_overhead_mb=0 时,Dynamic 分量不含任何额外开销 | 回归:现有 UT 通过(但需注意 TP=2 时 safety_buffer 从 1 GB 降为 0,有行为变化) |
| P1-06 | safety_buffer_mb=3072、tp_shardable_overhead_mb=0、TP=1 时,effective = 3072 MB | 偏差缩小约 2 GB |
| P1-07 | safety_buffer_mb=3072、tp_shardable_overhead_mb=2676、TP=1 时,两项均计入 | 总增量 = 3072 + 2676 = 5748 MB(需确认不致过度高估) |
P2 测试
| 用例 ID | 描述 | 期望 |
|---|---|---|
| P2-01 | expert_zero3_sharding=True、EP=2→8 时,routed expert param 不随 EP 变化 | 与当前行为完全一致(抵消) |
| P2-02 | expert_zero3_sharding=False、EP=2→4→8 时,routed expert param 按 1/EP 线性缩放 | EP=4 时 param = EP=2 时的 50%;EP=8 时 = 25% |
| P2-03 | expert_zero3_sharding=False 时,OS 和 Grad 也随 EP 线性缩放 | OS 和 Grad 的缩放比例与 param 相同 |
| P2-04 | expert_zero3_sharding=False 时,gradient clipping buffer(body.py:130)也随 EP 线性缩放 | Grad-clip 的缩放比例与 param 相同 |
| P2-05 | expert_zero3_sharding 不影响 shared expert 的内存估算 | shared expert 的 param/OS/grad 在两种设置下完全相同 |
| P2-06 | expert_zero3_sharding 不影响 non-expert 的内存估算 | non-expert 的 param/OS/grad 在两种设置下完全相同 |
| P2-07 | has_op=False 时,expert_zero3_sharding=True 和 False 的结果相同 | 两种设置的 shard_p_os_exp 均为 cp * t_exp |
| P2-08 | 默认值 expert_zero3_sharding=True 时,所有现有 UT 无回归 | 全部通过 |
P3 测试
| 用例 ID | 描述 | 期望 |
|---|---|---|
| P3-E1 | 消融实验 EXP-1:从 time_sum 移除 EP_COMM,6 config 的 BUBBLE 变化方向 | 记录方向,用于决定 §4.3.2 的修法 |
| P3-E2 | 消融实验 EXP-2:移除 DP=0 hack,6 config 的 BUBBLE 变化方向 | 记录方向,判断 DP 是否为主因 |
| P3-E3 | 消融实验 EXP-3:固定 EP=4,仅改变 DP(4/8/16),PP_WAIT 变化 | 隔离 DP 对 PP_WAIT 的影响 |
| P3-E4 | 消融实验 EXP-4:6 config 的 straggler stage id | 检查是否随 EP 变化 |
| P3-01 | ep_bubble_overlap_ratio=1.0 时,PP_WAIT 随 EP 增大而减小 | PP_WAIT 排序与实测一致(待 EXP-1 确认方向后更新期望) |
| P3-02 | ep_bubble_overlap_ratio=0.0 时,行为与修改前一致 | PP_WAIT 值与修改前相同 |
| P3-03 | ep=1(无 EP 通信)时,overlap_ratio 不生效 | PP_WAIT 值与修改前相同 |
| P3-04 | 移除 DP=0 hack 后,DP 通信正确参与计算 | comm[Dim.DP] > 0,TOTAL 包含 DP 通信分量 |
P4 测试
| 用例 ID | 描述 | 期望 |
|---|---|---|
| P4-01 | EP 通信经过 estimate_comm_score 转换后,EP_WAIT 单位为秒 | EP_WAIT 绝对值与实测差距从 ~1e7 倍缩小到 10 倍以内 |
| P4-02 | EP_WAIT 不再淹没 TOTAL 的其他分量 | EP_WAIT 占 TOTAL 的比例 < 50% |
| P4-03 | P3+P4 修完后,TOTAL Pearson r > 0.5 | 6 config 的 TOTAL 排序与实测基本一致 |
| P4-04 | 无 EP(ep=1)时,TOTAL 行为与修改前一致 | 回归:现有 UT 通过 |
| P4-05 | ttype != TIME 时,EP 通信不经过 estimate_comm_score 转换 | EP 仍为字节数(与 DP/TP 行为一致) |
5.2 集成测试
| 用例 ID | 描述 | 期望 |
|---|---|---|
| INT-01 | DeepSeek-V3 缩小版 6 config 全量回归:P1+P2 修完后,Peak HBM 预估/实测比均在 0.85-1.10 | TP=1 组从 0.72-0.75 提升到目标范围 |
| INT-02 | DeepSeek-V3 缩小版 6 config 全量回归:P3+P4 修完后,PP_WAIT Pearson r > 0.5,TOTAL Pearson r > 0.5 | 排序正确,无负相关 |
| INT-03 | 非 MoE 模型(n_exp=1)回归:所有变更对 dense 模型无影响 | 预估结果与修改前完全一致 |
| INT-04 | P1 两个参数交互:safety_buffer_mb=3072 + tp_shardable_overhead_mb=2676,TP=1 和 TP=2 的预估均不过度高估 | 预估/实测比均在 0.85-1.10 |
5.3 回归测试
- 现有
test_sapp_nd系列 UT 全部通过 - 现有
test_memory_estimation系列 UT 全部通过 - 现有
test_comm_estimation系列 UT 全部通过 - 原有输出 shape 与 dtype 保持不变
6. 风险与开放问题
| 风险 / 开放问题 | 缓解 / 决策 |
|---|---|
P1:tp_shardable_overhead_mb 的默认值 2676 MB 仅基于 DeepSeek-V3 缩小版,不同模型的运行时开销比例不同 |
优先通过 fast-tuner 校准自动设置;短期文档化推荐值的适用范围 |
| P1:safety_buffer 改为 /t 缩放后,TP=2 时从 1 GB 降为 512 MB,可能轻微减少安全余量 | TP=2 原本已高估 5%,减小 safety buffer 可缓解高估;若担心可设 safety_buffer_mb=2048 使 TP=2 时仍有 1 GB |
| P1:safety_buffer_mb 和 tp_shardable_overhead_mb 同时设置时可能过度高估 | 需用 INT-04 验证;推荐用法是二选一或微调 |
P2:expert_zero3_sharding 默认 True 保持向后兼容,但大多数 MoE 模型可能都需要设为 False |
收集更多 MoE 模型的 ZeRO-3 行为后,考虑在未来版本调整默认值 |
| P3:根因方向已修正——BUBBLE 随 EP 增大(而非减小),但 overlap 修法需消融实验验证 | §4.3.1 的消融实验优先级最高;数学推导确认了 BUBBLE 增大方向,但 overlap_ratio 修法的效果需实测验证;若 EXP-1 显示 DP 减少效应是主因,则需改修 DP hack(§4.3.3) |
P3:ep_bubble_overlap_ratio=1.0 假设 EP 通信完全在 bubble 内完成,这对不同集群拓扑不一定成立 |
提供配置项让用户调整;长期可通过 fast-tuner 自动校准 |
P3:DeepSeek-V3 的 dense/MoE 混合结构导致 EP_COMM 在 stage 间不均匀(arch_hooks.py:218-231),overlap 修正需正确处理 dense stage(ep=1, 无 EP_COMM)和 MoE stage 的差异 |
overlap 扣除仅对 ep>1 的 stage 生效,dense stage 的 straggler_time 不变 |
| P3:Pipeline schedule bubble 比例公式本期仅预留接口,不实现 | 标记为 follow-up issue,待 fast-tuner pipeline conductor 同步修改后实现 |
P4:EP 通信转时间依赖 device.level_bandwidth 的准确性,带宽配置错误会导致 EP_WAIT 绝对值偏差 |
文档化带宽配置要求;fast-tuner 可用实测 EP_WAIT 校准带宽参数 |
| P4:DP 通信返回 element count 但被 estimate_comm_score 当作字节数处理,存在单位不一致 | 本期不处理,标记为 follow-up;需确认 DP comm 的 bytes 转换是否在其他位置已完成 |
| P4:CP 通信也从未被 estimate_comm_score 转换 | 本期不处理,标记为 follow-up issue |
P4:comm_time.py:396-397 的 TO REMOVE hack(comm[Dim.TP] /= 2 和 comm[Dim.DP] = 0)需与 P3 §4.3.3 联动清理 |
移除前需消融实验确认影响;移除后用 UT 验证无回归 |
| P3→P4 依赖:TOTAL 的 Pearson r 改善依赖 P3 先修完 | 修复顺序:P2 → P1 → P3(消融实验)→ P3(实施)→ P4 |
7. 验收标准
- P1:TP=1 配置的预估/实测比从 0.72-0.75 提升到 0.85-1.10,TP=2 配置保持在 1.05-1.10
- P2:
expert_zero3_sharding=False时,expert param/OS/grad/grad-clip 随 EP 线性缩放(不再被抵消);expert_zero3_sharding=True时行为与现有完全一致 - P3:PP_WAIT Pearson r 从 -0.197 提升到 > 0.5,6 config 排序与实测一致;EP_WAIT Pearson r 不退化(保持 > 0.85)
- P4:EP_WAIT 绝对值与实测差距从 ~1e7 倍缩小到 10 倍以内;TOTAL Pearson r 从 -0.534 提升到 > 0.5(P3 修完后评估);各分量对 TOTAL 的贡献比例合理,不出现单一分量 > 90%
- 全部现有 UT 无回归
- 非 MoE 模型(n_exp=1)的预估结果与修改前完全一致
8. 优先级与依赖关系
| 优先级 | 问题 | 影响 | 依赖 | 修复难度 |
|---|---|---|---|---|
| P1 | TP 基线偏差 | OOM 误判风险 | 无 | 中(需建模开销项) |
| P2 | EP/ZeRO-3 抵消 | EP 策略筛选失真 | 无 | 低(只需加配置项) |
| P3 | PP_WAIT 负相关 | PP 性能估算反转 | 消融实验结果 | 高(需确认根因后再建模) |
| P4 | TOTAL 负相关 | 总时间不可靠 | P3 | 中(带宽转换 + 依赖 P3) |
建议修复顺序:P2 → P1 → P3(消融实验)→ P3(实施)→ P4。
9. 参考
- PR #827 实测验证数据:
exp_peak_hbm_validation.xlsx、exp_ep_wait_correlation.xlsx - QA.md Q9:HBM 预估偏差拆解分析
body.pystat_p_layer / stat_os_layer / stat_grad_layer(lines 54-89, 130)_cost_model_parser.pyshard_p_os_exp / d_exp 计算(lines 59-61, 131, 135, 137)_cost_model_variables.py_CostModVar dataclass(lines 34-240)comm.pyep_comm_layer_balanced / ep_comm_layer_imbalanced(lines 167-226)comm_time.pyestimate_from_mem_comm / estimate_comm_score(lines 314-425, 489-507)estimate.pyBUBBLE / PP_COMM / TOTAL 计算(lines 238-333, 336-369, 467-499)debug.pyPerfParts → RealParts 映射(lines 567-594)arch_hooks.pyhook_dense / hook_moe 的 ep 设置(lines 218-231)_backbone.pysafety buffer 硬编码(lines 660-662)
schema_version: 1
source: gitcode
gitcode_repo: mindspore/hyper-parallel
gitcode_issue: 272
source_url: https://gitcode.com/mindspore/hyper-parallel/issues/272
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the six-configuration profiling data, then trace the named paths in _backbone.py, _cost_model_parser.py, _cost_model_variables.py, comm_time.py, estimate.py, and debug.py. Review the existing memory, sharding, pipeline, and communication-unit formulas before changing their configuration and conversion behavior. Done means the P1-P4 targets and correlation thresholds in section 3 are met without altering the listed non-goals.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- distributed-systems, machine-learning, performance
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100