已开启
DTensor to()/float() 方法统一至核心层 #112
sargerasking创建于 4月27日
4月27日 关联了pull request:feat: unify DTensor to()/float() to core layer with hardware-agnostic UT
4月27日 关联了pull request:feat: unify DTensor to()/float() to core layer with hardware-agnostic UT
Mmindspore-ci-bot
3 天前 关联了pull request:[mirror] mindspore-ai/hyper-parallel#1053: feat(cp): add MLA Ulysses context parallelism to JT DeepSeek V3
3 天前 关联了pull request:[mirror] mindspore-ai/hyper-parallel#1053: feat(cp): add MLA Ulysses context parallelism to JT DeepSeek V3
DTensor
to()/float()方法统一至核心层设计1. 背景
1.1 现状
HyperParallel 的
DTensorBase分别在两个平台实现中定义:platform/torch/dtensor.py→DTensorBase(Tensor)torch.Tensorto()/float()platform/mindspore/dtensor.py→DTensorBase(Tensor)mindspore.Tensorto()/float()core/dtensor/dtensor.py→DTensor(DTensorBase)to()/float()当前
to()仅在 Torch 后端的DTensorBase中实现,MindSpore 后端缺失。若在 MindSpore 的DTensorBase中补齐to(),会产生两份逻辑完全相同的代码,违反 DRY 原则。1.2 核心困难
MindSpore C 描述符限制:MindSpore 的
Tensor是 C 扩展类型,其to方法在元类创建阶段被包装为 C-level 描述符,该描述符强制isinstance(self, Tensor)类型检查。因此:DTensorBase.to()DTensorBase.__dict__["to"]会抛出KeyError,因为元类已将其消费Tensor子类)都会触发descriptor 'to' for 'TensorPy' objects doesn't apply to ...错误而 PyTorch 后端的
torch.Tensor.to是纯 Python 实现,不存在此问题,因此 Torch 后端可以写 UT。1.3 关键发现
DTensor(定义在core/dtensor/dtensor.py)是一个纯 Python 类(继承自DTensorBase,但自身没有 C 扩展限制)。因此DTensor.__dict__["to"]可以正常获取到绑定的 Python 函数——这为跨平台 UT 提供了可行路径。2. 目标与非目标
2.1 目标
to()和float()方法统一放在core/dtensor/dtensor.py的DTensor类中,消除平台间重复代码_alias_placements()辅助方法,统一"获取有效 placements"的逻辑to()/float()的正确性2.2 非目标
detach()/detach_()/type()等方法(它们逻辑相同但涉及平台特定行为,可作为后续工作)DTensorBase.__new__/__init_data__等构造逻辑3. 设计方案
3.1 方法位置迁移
3.2 实现细节
_alias_placements()辅助方法:def _alias_placements(self) -> Sequence[Placement]: """Return alias_placements from layout, falling back to _placements.""" if hasattr(self, '_layout') and self._layout: return self._layout.alias_placements return self._placements此方法将 Torch 后端
DTensorBase中反复出现的模式self._layout.alias_placements if hasattr(self, '_layout') and self._layout else self._placements抽取为可复用的方法。to()方法:def to(self, *args, **kwargs): """Move the DTensor to a different device or dtype.""" new_local = self._local_tensor.to(*args, **kwargs) return self.__class__(new_local, device_mesh=self._device_mesh, placements=self._alias_placements())float()方法:def float(self): """Convert the DTensor to float dtype.""" new_local = self._local_tensor.float() return self.__class__(new_local, device_mesh=self._device_mesh, placements=self._alias_placements())3.3 方法解析顺序(MRO)分析
dt.to(...)先在DTensor.__dict__中找到to,不再查找DTensorBase或Tensor的toDTensor的 Python 实现,绕过 C 描述符限制DTensorBase.to()完全一致,因为逻辑相同3.4 UT 测试策略
核心技巧:通过
DTensor.__dict__["to"]提取纯 Python 函数,在非 Tensor 对象上调用:_to_fn = DTensor.__dict__["to"] # _Recorder 是一个普通 Python 类(不继承 DTensor/Tensor) # 用 Mock() 作为 _local_tensor,避免硬件访问 fake = _Recorder.__new__(_Recorder) fake._local_tensor = Mock() # mock,不触碰真实硬件 fake._layout = SimpleNamespace(alias_placements=[Shard(0)]) _to_fn(fake, "float16_arg") # 直接调用 Python 函数测试用例:
test_to_delegates_dtype_conversion_to_local_tensorto()正确转发参数给_local_tensor.to()test_to_preserves_device_meshdevice_mesh不变传递test_to_uses_alias_placements_when_layout_exists_layout时使用alias_placementstest_to_falls_back_to_placements_when_no_layout_layout为 None 时回退到_placementstest_float_delegates_to_local_tensorfloat()正确调用_local_tensor.float()4. 影响范围
4.1 受影响文件
core/dtensor/dtensor.py_alias_placements()、to()、float()platform/torch/dtensor.pyto()、float()(逻辑已上移至核心层)tests/ut/core/dtensor/test_dtensor_to.py4.2 不受影响
tests/torch/dtensor/dtensor_api.py不变,DTensor.to()行为一致DTensor.to()to()/float()不经过 op dispatch_alias_placements/to/float的测试不受影响5. 验证方案
pytest tests/ut/core/dtensor/test_dtensor_to.py -v(5 个用例)pytest tests/torch/dtensor/test_dtensor_api.py -v(已有用例)