mindspore-ai / mindspore-ai/hyper-parallel

SAPP-ND 实测验证发现的问题(P1-P4)

Open
#230 0 comments 0 reactions 0 assignees View on GitHub

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_buffer
  • body.py:62 — non-expert param 使用 shard_p_os_non_exp_partial,当 has_op=True 时为 os_max_shard * cp(非简单 /t),当 has_op=False 时为 t * cp
  • body.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,137config_dp_tp_exp() 计算 d_exp(三个分支均含 /ep
  • _cost_model_parser.py:59-61config_optimizer_shard() 计算 shard_p_os_exp
  • _cost_model_parser.py:62-66shard_grad_exp(当 has_grad_shard 时等于 shard_p_os_exp)
  • body.py:58stat_p_layer 中 routed_mem 公式
  • body.py:72stat_os_layer 中 routed_mem 公式
  • body.py:84stat_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_CostModVar dataclass 中目前无 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_COMMdebug.py:590-593),其中:

  1. BUBBLE = pipeline_perf - time_sumestimate.py:325-332),time_sum 包含所有 PerfParts(含 EP_COMM)
  2. PP_COMM 使用硬编码的 MANUAL_P2P_RATIO = 0.002estimate.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-231hook_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] = 0comm_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-333estimate_pipeline() 计算 BUBBLE
  • estimate.py:295-313 — time_sum 累加循环(含 EP_COMM)
  • estimate.py:336-369estimate_p2p_comm() 计算 PP_COMM
  • estimate.py:467-499apply_regression_coefficients() 独立缩放各 PerfParts
  • debug.py:587-593estimation_in_real_parts() 中 EP_WAIT = EP_COMM、PP_WAIT = BUBBLE + PP_COMM 的映射
  • arch_hooks.py:218-231hook_dense 设置 ccfg.ep=1hook_moe 设置 ccfg.ep=saved.ep,导致 EP_COMM 在 stage 间不均匀
  • comm_time.py:397comm[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 负相关由两个独立问题叠加导致:

  1. PP_WAIT 负相关(P3) 直接把 TOTAL 排序带偏
  2. 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-181ep_comm_layer_balanced() 返回字节数,docstring 明确标注 "Result is in bytes"
  • comm.py:184-226ep_comm_layer_imbalanced() 同样返回字节数
  • comm_time.py:366-374关键estimate_comm_score() 转换循环为 [Dim.DP, Dim.TP, Dim.DP],不含 Dim.EPDim.CP
  • comm_time.py:489-507estimate_comm_score() 将字节除以各网络层级带宽得到时间
  • comm_time.py:396-397comm[Dim.TP] /= 2comm[Dim.DP] = 0(TO REMOVE hack)
  • estimate.py:492-499TOTAL = COMP + DP_WAIT + MP_WAIT + EP_WAIT + CP_WAIT + PP_WAIT 的累加
  • debug.py:587-589EP_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-662safety_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_ophas_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.pyep_comm_layer_balanced()ep_comm_layer_imbalanced() 返回字节数。comm_time.py:366-374estimate_comm_score() 的转换循环为 [Dim.DP, Dim.TP, Dim.DP]不含 Dim.EP——无论 ttype 是否为 TIME,EP 从未被转换。

变更:在 comm_time.pyestimate_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.pyep_comm_layer_balanced() 保持返回字节数(不改函数签名)
comm.pyep_comm_layer_imbalanced() 保持返回字节数(不改函数签名)
comm_time.py:366-374 — 转换循环 Dim.EP 加入循环,调用 estimate_comm_score(comm[Dim.EP], Dim.EP, overlap=0.0) 转换为时间
debug.pyestimation_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-397TO REMOVE hack(与 P3 §4.3.3 联动)
4.4.2 带宽参数

estimate_comm_score() 已有完整的带宽模型(comm_time.py:489-507),使用 device.level_bandwidthdevice.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-397TO REMOVE hack(comm[Dim.TP] /= 2comm[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.xlsxexp_ep_wait_correlation.xlsx
  • QA.md Q9:HBM 预估偏差拆解分析
  • body.py stat_p_layer / stat_os_layer / stat_grad_layer(lines 54-89, 130)
  • _cost_model_parser.py shard_p_os_exp / d_exp 计算(lines 59-61, 131, 135, 137)
  • _cost_model_variables.py _CostModVar dataclass(lines 34-240)
  • comm.py ep_comm_layer_balanced / ep_comm_layer_imbalanced(lines 167-226)
  • comm_time.py estimate_from_mem_comm / estimate_comm_score(lines 314-425, 489-507)
  • estimate.py BUBBLE / PP_COMM / TOTAL 计算(lines 238-333, 336-369, 467-499)
  • debug.py PerfParts → RealParts 映射(lines 567-594)
  • arch_hooks.py hook_dense / hook_moe 的 ep 设置(lines 218-231)
  • _backbone.py safety 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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.