已开启
[RFC]: distributed_checkpoint 去 platform 抽象,收敛为 PyTorch-only #387
zhangbuxue创建于 17 天前
17 天前 添加了label:RFC
Zzhangbuxue
17 天前 关联了pull request:refactor(dcp): drop the platform abstraction from distributed_checkpoint
17 天前 关联了pull request:refactor(dcp): drop the platform abstraction from distributed_checkpoint
17 天前 修改了issue 的描述
17 天前 修改了issue 的描述
17 天前 修改了issue 的描述
动机(Motivation)
hyper_parallel/core/distributed_checkpoint/(下称 DCP)目前通过get_platform()抽象层同时支持 PyTorch 与 MindSpore 两个后端,代价是:platform.xxx()调用,共 100+ 处。读代码时无法直接看出一次platform.detach()/platform.broadcast_async()究竟落到哪个框架的哪个 API。filesystem_storage.py中_fetch_tensor_file/_open_checkpoint_files/_broadcast_batch_bytes各带一套PlatformType.PYTORCH分支与 MindSpore 对侧实现;offline_transform.py的类型校验与 checkpoint 格式推断同样双份。tests/mindspore/st/distributed_checkpoint/的离线转换 ST 早已失效(MindSporeTensor无.detach方法),在本次改动之前就无法通过。抽象层带来的可读性与维护成本已显著超过其价值。DCP 收敛为 PyTorch-only 后,可直接使用 torch 语义,并顺带清理掉因双后端而存在的冗余结构。
目标设计
① 去 platform,直调 torch
get_platform()/platform.xxx()全部替换为对应 torch 调用:platform.get_rank()/get_world_size()/barrier()dist.get_rank()/dist.get_world_size()/dist.barrier()platform.broadcast_async(t, src, g)dist.broadcast(t, src, g, async_op=True)platform.new_group(ranks)/destroy_process_group(g)dist.new_group(ranks=list(ranks))/dist.destroy_process_group(g)platform.all_gather_object(...)dist.all_gather_object(...)platform.detach(t)/platform.is_tensor(o)t.detach()/isinstance(o, torch.Tensor)platform.empty(...)/platform.new_tensor(...)torch.empty(...)platform.Tensor/platform.dtypetorch.Tensor/torch.dtypetorch 无单一对应调用的(checkpoint 文件读写、通信组缓存查询、设备流同步、fused copy、dtype 字符串解析),在 DCP 目录内自行封装,不再穿透到 platform 层。
删除全部
PlatformType分支及 MindSpore 对侧实现:_fetch_ms_tensor_file、_MS_FILES_KEPT、_broadcast_batch_bytes、src_platform="mindspore"。②
util.py按职责拆分原
util.py达 960 行,混杂多种关注点。拆为(并顺带把util.py更名为utils.py、ragged_utils.py更名为ragged.py,使目录里只剩一个公共工具模块):checkpoint_io.pybroadcast.pyutils.py③ 删除 DCP 不依赖的模块
经全仓库依赖排查,以下模块在 DCP 内部零使用,仓库内引用方仅为其自身 UT 与
__init__.py的 re-export:loader.py/saver.py:单文件 safetensors 读写包装(各约 40 行)layout.py:layout 采集/落盘/跨 rank 汇总(153 行)reshard.py的ReshardHandler及其辅助函数(约 300 行)ensure_broadcast_groups:_build_broadcast_groups的薄封装,引入时即无生产调用方,加载路径走broadcast_groups_for_load其中
infer_intersection(计算两个分片区间的重叠)是reshard.py中唯一被 DCP 依赖的函数——standard_planner.py在构造 read item 时用它计算本地分片与已存分片的交集——移入utils.py,与产出其输入的chunk_to_area相邻。需要设计评审DFX建议
替代方案比较
filesystem_storage.py减少约 90 行;DCP 目录自洽get_created_group/synchronize/copy_each等无 torch 单一对应,会在多个调用点重复实现选 B。判断依据是 MindSpore 侧已无维护需求,且
core/dtensor/已有 torch-only 的先例(pylint 插件的TORCH_ONLY_CORE_PARTS已包含该目录)。对现有代码的影响范围
39 个文件,+1796 / −3285(净减 1489 行)。
broadcast.py、checkpoint_io.py、tests/ut/core/distributed_checkpoint/test_broadcast.pyloader.py、saver.py、layout.py、reshard.py及其 UT/ST(共 9 个文件)core/dtensor/零改动——DCP 仅引用其原生的Layout与infer_slice_area_by_layoutscripts/pylint_hyperparallel.py:将distributed_checkpoint/加入TORCH_ONLY_CORE_PARTS,与既有的core/dtensor/同列向后兼容性
hyper_parallel.core.distributed_checkpoint.__all__移除 8 个导出:save_checkpoint、load_checkpoint、get_current_layout、get_global_layout、save_layout、load_layout、combine_layout、ReshardHandler。已确认这 8 个名字在仓库内(
hyper_parallel/、tests/、scripts/、examples/)均无实际调用方,仅存在于__init__.py的 re-export 与其自身 UT 中。若外部用户有依赖,需在 release note 中说明。save/async_save/load/get_optim_state_dict/set_optim_state_dict及全部 Planner / Storage 接口不变。顺带修复的缺陷
StandardLoadPlanner.acquire_tensor的两条分支中,只有非 ragged 分支做了detach;ragged 分支经get_ragged_box_tensor返回的是带grad_fn的 view。实测对这种 view 做copy_会抛:此前未暴露,是因为
filesystem_storage._validate_and_copy_tensor有一层兜底detach。本次将两条分支统一为先选出本地分片、再统一detach+ narrow,并把「返回值必须 detach」写入LoadPlanner.acquire_tensor抽象接口契约,兜底层随之移除——责任从调用方收回到实现方。性能影响
无。所有替换均为同语义直调(
platform.get_rank()本就是dist.get_rank()的转发)。广播的批量合并阈值DEFAULT_BROADCAST_BATCH_BYTES及在途上限_MAX_BROADCASTS_IN_FLIGHT行为不变;_open_checkpoint_files在 torch 路径上的保留文件数仍为 8。已知遗留
api.py与async_persist.py之间存在 pylintR0401 cyclic-import。该循环在本次改动前即存在,由维护者有意通过函数内import在运行时打断(async_persist中两处from ...api import _save_impl,带# pylint: disable=import-outside-toplevel)。本 RFC 不处理;彻底消除需将 storage 协同(FileType/gather_all_results_from_storage)与_save_impl各自拆出独立模块,属于另一项重构。相关的RFCs和API
对外接口变更
hyper_parallel.core.distributed_checkpoint的 8 个__all__导出(见上)LoadPlanner.acquire_tensor的返回值必须为 detached view(自定义 Planner 实现方需注意)convert_full_checkpoint_to_dcp的src_platform取值从Literal["torch", "huggingface", "mindspore"]收窄为Literal["torch", "huggingface"]依赖关系
本 RFC 不依赖其他 RFC。与
core/dtensor/收敛为 torch-only 的既有决策方向一致(该目录已在 pylint 插件中登记为 torch-only)。文档同步更新:
docs/guide/distributed_checkpoint.md的模块表、示例与正文中残留的platform.*提法。完整的反馈期限
一周。
CC List
(待补充)
其他补充说明
验证情况
.pylintrc+scripts/pylint_hyperparallel.py):改动涉及的 35 个文件 0 告警(除上述改动前即存在的R0401)tests/ut/core/distributed_checkpoint+tests/ut/core/dtensor591 passed / 98 skippedtests/ut:与同机 upstream master 基线逐条比对,5 个失败两侧完全相同且均在tests/ut/platform/**,零新增失败MindSpore 相关 ST(
tests/mindspore/st/distributed_checkpoint/)随实现一并删除。Before submitting a new issue...