已开启
CC分析fully_shard/DTensor相关编码问题 #22
lishanni创建于 2月26日
yangzhenzhang
2月26日 评论:
2月26日 评论:
- is_partial 未加括号调用,条件永远为 True--------------------------需要处理一下
- self.platform 未定义 — ReduceScatter 路径必定崩溃-------------之前让佳琪处理中
- 无词表并行 Embedding 支持------------------------------------------之前让墨林处理中


fengyixing
2月26日 评论:
2月26日 评论:
P0级别3.DeviceMesh 缓存 key 使用完整 rank_list — 否则多 mesh 场景必出错 ---------冯义星处理


2月26日 关联了pull request:align_three_api_impl_size_numel_to
2月27日 关联了pull request:process group get local rank
2月28日 关联了pull request:fix_some_fully_shard_bug
yide12
3月2日 评论:
3月2日 评论:
p0-5 "set_requires_all_reduce 设置了错误的属性名" 已经提pr修改,https://gitcode.com/mindspore/hyper-parallel/pull/277


6月30日 关联了pull request:feat(dtensor): distribute_tensor scatter/broadcast with src_data_rank
6月30日 关联了pull request:feat(dtensor): distribute_tensor scatter/broadcast with src_data_rank
HyperParallel 框架深度分析报告
分析范围
对
fully_shard()强相关的 DTensor 核心(Layout / Placement / DeviceMesh / Redistribution)、FSDP/HSDP 训练流水线(参数分片/聚合/梯度同步)以及分布式算子推导进行了全面审计。以下按 严重程度 分级汇报所有发现的问题。一、致命级别 (Critical) — 会导致运行崩溃或静默产生错误结果
1.
is_partial未加括号调用,条件永远为 TrueLayout.is_partial是一个 普通方法(非@property):不加
()取到的是方法对象本身,Python 中任何方法对象都是 truthy。后果:reset_partial()永远被调用,即使没有 partial 状态。这会篡改from_layout(即源 DTensor 的 layout),可能导致共享同一 layout 对象的其他 DTensor 状态被破坏。not from_layout.is_partial永远为False,reduce_partial的 early-return 永远不触发,即使没有 partial 也会尝试执行 reduce。2.
self.platform未定义 — ReduceScatter 路径必定崩溃TensorRedistribution类没有self.platform属性;模块级变量是platform。当重分布路径需要 ReduceScatter(Partial -> Shard 转换)时,会抛出AttributeError。这意味着 Partial -> Shard 重分布功能完全不可用。3. DeviceMesh 缓存 key 仅用首尾 rank — 不同拓扑可能返回错误 Mesh
只使用
rank_list的第一个和最后一个 rank 作为缓存 key。两个 shape 和 dim_names 相同但内部 rank 排布不同的 mesh(如(0,2,1,3)vs(0,1,2,3))会命中同一缓存。后果:返回错误的 DeviceMesh 对象,通信分组错误,数据放置错误。同样的缺陷存在于to_hash()方法中,会进一步传播到 redistribution 的缓存 key。4.
post_backward中reduced_grad可能未定义 — HSDP 场景崩溃当参数
shard_world_size == 1(未分片)但replicate_world_size > 1(有复制)时,if shard_world_size > 1分支被跳过,reduced_grad从未赋值。下方all_reduce_grad(grad=reduced_grad)将抛出UnboundLocalError。这个场景在 HSDP(2D mesh,部分维度复制、部分维度分片)下完全有可能出现。5.
set_requires_all_reduce设置了错误的属性名实际使用的属性名是
self.requires_all_reduce(在TorchHSDPStateV2.__init__第 51 行设置),但此 setter 写的是all_reduce_grads,完全是一个新属性。后果:用户调用set_requires_all_reduce(False)后静默无效,梯度的 AllReduce 仍然会执行。二、严重级别 (High) — 可能导致精度问题或特定场景下的错误
6. RNG offset 计算使用了 mesh shape 而非 tensor shape
应该使用分布式张量的全局 shape 而非设备网格 shape。这会导致各 rank 的 RNG offset 推进量错误,破坏随机数的确定性重现保证,进而导致 dropout 等操作在不同 rank 上不一致。
7.
need_synchronize变量作用域 Bug — 仅反映最后一个参数need_synchronize在 for 循环内赋值,但检查在循环外。只有最后一个参数的返回值生效。如果前面的参数需要同步(如 CPU offload)但最后一个不需要,同步会被跳过,导致 CPU offload 场景下的数据竞争。8. Layout
_infer_slice_area_by_rank不处理不均匀分片整数除法直接截断,不均匀分片时(如 shape=7 在 2 设备上分片),最后的元素被静默丢弃。PyTorch DTensor 会处理 remainder shard,这里缺失。
9. Norm 算子的输出 layout 可能错误
NormDistributedOp返回的out_layout将归一化维度设为"None"(不分片),这是 mean/rstd 辅助输出的 layout,而非主输出的 layout。主输出应保持与输入相同的 layout。三、性能级别 (Performance) — 严重影响训练效率
10. 所有通信操作均为同步执行 — 无计算与通信重叠
这是 最大的性能问题。
unshard 阶段(forward/backward 前的 all-gather):
每个参数的 all-gather 逐个同步执行,完成后才开始下一个。
post_backward 阶段(reduce-scatter + all-reduce):
两者均为同步操作(
async_op默认 False),没有计算与通信重叠的可能。PyTorch 原生 FSDP2 通过独立的 CUDA stream 实现 overlap,此处完全缺失。11. V2 路径无梯度 Bucketing 支持
配置硬编码关闭了通信融合和梯度融合。每个参数独立发起一次 reduce-scatter/all-reduce 小集合通信,大量小消息无法利用网络带宽。
12. Layout 构造存在双重 deepcopy
每次从 placement 构造 Layout 都执行两次
deepcopy,包括其中的 DeviceMesh(numpy 数组、缓存数据结构等)。这个路径是 DTensor 创建和重分布的热路径。13. reduce-scatter 每次反向传播都重新分配输出 buffer
不像 all-gather 有持久化 buffer 的机制,reduce-scatter 每次调用都
torch.empty,增加显存分配压力。14. SDPA 序列并行构造显式 causal mask 退化了 FlashAttention 的优化
当启用序列并行 + causal masking 时,代码物化了一个
[local_q_len, global_kv_len]的 bool mask。这使得 FlashAttention 内核无法使用其高效的内建 causal mask 优化,长序列下会产生巨大的额外显存开销。四、可扩展性问题 (Scalability)
15. DeviceMesh 中 rank 查找为 O(n) 线性搜索
出现在多个高频调用方法中(
get_rank_list_along_axis,get_devices_for_axis,get_local_rank)。在大规模集群(数百/数千设备)下会成为瓶颈。应使用预构建的rank -> index字典。16. Layout 缓存无上限 — 动态 shape 场景内存泄漏
Layout 缓存无淘汰策略,无容量上限。在变长序列或动态 shape 场景中(如 NLP 训练),缓存会无限增长。
17. 无词表并行 Embedding 支持
EmbeddingDistributedOp仅处理 embedding 维度(weight 最后一维)的分片。如果用户在词表维度(weight 第 0 维)做分片,会静默产生错误结果。大模型训练中词表并行是基本需求。18. Prefetch 需手动配置 — 无自动执行序分析
set_modules_to_forward_prefetch / set_modules_to_backward_prefetch预取需要用户手动指定模块依赖。PyTorch FSDP2 能自动从执行图推导预取顺序。当前实现对用户要求过高,且无法做到参数粒度的细粒度预取。
五、其他代码质量问题
Shard(0)和Replicate()哈希冲突placement_types.py:76,97__hash__都返回 0,降低 dict/set 性能unshard()中 raise 之后有死代码hsdp_state.py:129-131comm_fusion分支因 raise 永远不执行ignored_params参数从未被使用api.py:356+ 整个 init 链hsdp_params_with_grad/unsharded_grads声明未使用state.py:199-200Partial.__str__丢失 reduce_op 信息placement_types.py:134Partial("sum")和Partial("max")都显示为"P"avgreduction 的溢出风险tensor_redistribution.py:239-242六、总结与建议
优先级 P0 — 必须立即修复
is_partial加括号 — 影响所有重分布路径的正确性self.platform→platform— 否则 ReduceScatter 完全不可用post_backward中reduced_grad的 NameError — HSDP 配置下必崩set_requires_all_reduce属性名修正 — 否则 API 静默无效优先级 P1 — 影响精度和效率
need_synchronize作用域优先级 P2 — 提升可扩展性
整体来看,框架的架构设计(DTensor + Layout + Placement 抽象、HSDP scheduler/state/param 分层)是合理的,与 PyTorch FSDP2 的设计理念一致。但在 实现细节 上存在多个致命 bug(特别是 P0 的 5 个问题),以及在 通信性能 上与成熟框架有显著差距(同步通信、无 bucketing、无 overlap)。建议按优先级逐步修复,P0 问题应阻断合入。