MindSpore分布式算子开发指南:从mint.gather接口分析到通用实现流程
文档目标
本文档旨在为新入职员工提供MindSpore分布式算子开发的系统性指导,通过以mint.gather接口为具体案例,详细解析MindSpore的代码结构、接口与算子关系以及分布式算子实现机制,使新员工能够快速理解并上手开发分布式算子。
1. MindSpore接口体系与算子原语关系
1.1 核心用户接口类型
MindSpore为用户提供了多种层次的接口,这些接口最终都会映射到具体的算子原语(Primitive)执行。理解这些接口层次对于分布式算子开发至关重要。
1.1.1 NN接口
NN接口是面向神经网络层的高级封装接口,位于mindspore/nn/目录下。
特点:
- 封装了一个或多个算子原语的组合调用
- 提供状态管理(如权重参数、运行模式等)
- 面向神经网络构建场景
示例:
from mindspore import nn
class MyNetwork(nn.Cell):
def __init__(self):
super().__init__()
self.conv = nn.Conv2d(3, 64, kernel_size=3) # 内部调用Conv2D算子
self.dense = nn.Dense(64, 10) # 内部调用MatMul算子
def construct(self, x):
x = self.conv(x)
x = self.dense(x)
return x
NN接口与算子原语的关系:
nn.Conv2d (高级封装)
├── Conv2D算子原语
├── BiasAdd算子原语
└── 可选的BatchNorm算子原语
1.1.2 functional接口
functional接口提供函数式编程风格的接口,位于mindspore/ops/目录下。
特点:
- 直接映射到具体的算子原语
- 无状态,纯函数式调用
- 适合函数式编程风格
示例:
from mindspore import ops
x = ops.add(a, b) # 直接调用Add算子原语
y = ops.matmul(m, n) # 直接调用MatMul算子原语
z = ops.reshape(x, shape) # 直接调用Reshape算子原语
functional接口与算子原语的关系:
ops.add(x, y)
└── Add算子原语 (直接映射)
ops.matmul(x, y)
└── MatMul算子原语 (直接映射)
1.1.3 Tensor接口
Tensor接口是Tensor对象上的方法接口,部分实现会调用对应的算子原语。
特点:
- 面向对象风格,操作更直观
- 部分方法直接调用算子原语
- 部分方法为Python层面实现
示例:
import mindspore
from mindspore import Tensor
x = Tensor([1, 2, 3, 4])
y = x.sum() # 调用ReduceSum算子
z = x.reshape((2, 2)) # 调用Reshape算子
w = x.astype(mindspore.float32) # 调用Cast算子
1.1.4 魔法方法
魔法方法是Python运算符重载方法,内部通常关联到相应的算子原语调用。
特点:
- 提供直观的运算符语法
- 底层调用对应的算子原语
- 支持自动微分
示例:
import mindspore
from mindspore import Tensor
a = Tensor([1, 2, 3])
b = Tensor([4, 5, 6])
c = a + b # __add__ -> Add算子
d = a - b # __sub__ -> Sub算子
e = a * b # __mul__ -> Mul算子
f = a @ b # __matmul__ -> MatMul算子
1.2 mint接口说明
1.2.1 mint接口定位
mint接口是MindSpore提供的与PyTorch接口高度对齐的接口集合,位于mindspore/mint/目录下。mint模块不仅包含functional接口,还包含完整的神经网络层接口(NN接口)。
设计目标:
- 降低PyTorch用户迁移到MindSpore的成本
- 提供与PyTorch一致的接口体验
- 简化模型迁移工作
mint模块目录结构:
mindspore/mint/
├── __init__.py # 导出functional接口
├── nn/ # 神经网络模块(NN接口)
│ ├── __init__.py
│ ├── functional.py # nn.functional接口
│ └── layer/ # 神经网络层
│ ├── activation.py # 激活函数层
│ ├── conv.py # 卷积层
│ ├── normalization.py # 归一化层
│ ├── pooling.py # 池化层
│ └── ...
├── optim/ # 优化器模块
├── linalg/ # 线性代数模块
├── special/ # 特殊函数模块
└── distributed/ # 分布式通信模块
1.2.2 mint接口类型
mint模块提供两类主要接口:
1. functional接口(位于mindspore/mint/):
- 数学运算:
abs,sin,cos,matmul,bmm,mm等 - 数组操作:
cat,stack,reshape,transpose,permute等 - 归约操作:
mean,sum,max,min,argmax等
2. NN接口(位于mindspore/mint/nn/):
| 接口类型 | 包含接口 |
|---|---|
| 卷积层 | Conv1d, Conv2d, Conv3d, ConvTranspose2d |
| 归一化层 | BatchNorm1d/2d/3d, LayerNorm, GroupNorm, SyncBatchNorm |
| 激活函数 | ReLU, SiLU, Sigmoid, GELU, Tanh, ELU, Mish等 |
| 池化层 | MaxPool2d, AvgPool1d/2d/3d, AdaptiveAvgPool1d/2d/3d |
| 全连接层 | Linear |
| 损失函数 | CrossEntropyLoss, MSELoss, BCELoss, NLLLoss等 |
| 其他 | Dropout, Embedding, Flatten, Upsample等 |
1.2.3 mint接口与标准接口的区别
| 特性 | mint接口 | 标准接口 |
|---|---|---|
| 命名风格 | PyTorch风格 | MindSpore原生风格 |
| 参数顺序 | 与PyTorch一致 | MindSpore自定义 |
| 默认值 | 与PyTorch一致 | MindSpore自定义 |
| 接口范围 | functional + NN完整接口 | 完整接口集 |
1.2.4 mint接口在MindSpore接口体系中的位置
用户代码
│
├── mindspore.mint.gather() ─┐
│ │ 映射
├── mindspore.mint.nn.Conv2d() ─┤
│ │
├── mindspore.ops.gather_d() ─┘
│
└── mindspore.ops.GatherD() ────────────> Primitive算子原语
│
▼
底层算子实现
2. 算子代码自动生成流程分析
2.1 gen_ops.py执行与功能解析
2.1.1 脚本概述
gen_ops.py是MindSpore框架的算子代码自动生成工具,位于mindspore/python/mindspore/ops_generate/gen_ops.py。
核心功能:
- 根据YAML配置文件自动生成算子相关的Python和C++代码文件
- 实现声明式算子定义,减少重复代码编写
- 保证代码一致性和可维护性
2.1.2 执行流程
def main():
resource_mgr = prepare_resources() # 1. 准备资源(加载YAML配置)
for generator in module_generators: # 2. 依次执行各模块生成器
generator(resource_mgr)
gen_utils.clear_obsolete_auto_gen_files() # 3. 清理过期的自动生成文件
2.1.3 五大生成模块
| 序号 | 生成器 | 功能 | 输出路径 |
|---|---|---|---|
| 1 | generate_ops_py_files |
生成Python算子定义文件 | mindspore/python/mindspore/ops/auto_generate/ |
| 2 | generate_ops_def_files |
生成C++算子定义文件 | mindspore/ops/op_def/auto_generate/ |
| 3 | gen_pyboost_code |
生成PyBoost加速相关代码 | mindspore/ccsrc/pynative/forward/pyboost/auto_generate/ |
| 4 | generate_aclnn_reg_file |
生成ACLNN内核注册代码 | mindspore/ops/kernel/ascend/aclnn/ |
| 5 | generate_api_files |
生成API接口定义文件 | mindspore/ops/api_def/ |
2.1.4 资源加载机制
def prepare_resources() -> ResourceManager:
resource_mgr = ResourceManager()
resource_mgr.register_resource(OpProtoLoader()) # 算子原型
resource_mgr.register_resource(DeprecatedOpProtoLoader()) # 废弃算子
resource_mgr.register_resource(FuncOpProtoLoader()) # 函数算子
resource_mgr.register_resource(OpDocYamlLoader()) # 文档
resource_mgr.register_resource(TensorMethodDocYamlLoader())
resource_mgr.register_resource(MintFuncDocYamlLoader())
resource_mgr.register_resource(OpApiProtoLoader(...)) # API原型
return resource_mgr
2.2 YAML定义文件与代码生成
2.2.1 目录结构
mindspore/ops/op_def/
├── yaml/ # 主要算子YAML定义目录
│ ├── doc/ # 文档YAML定义
│ ├── communication/ # 通信算子YAML定义
│ ├── infer/ # 推理相关算子
│ ├── gather_op.yaml # gather算子定义
│ ├── gather_d_op.yaml # gather_d算子定义
│ └── ... (约400+个算子定义文件)
├── deprecated/ # 已废弃的算子YAML定义
├── func_op/ # 函数式算子YAML定义
├── ops_def.cc # 算子定义C++源文件
└── ops_def.h # 算子定义头文件
2.2.2 YAML文件结构解析
以gather_d_op.yaml为例:
#operator gather_d
gather_d:
args: # 输入参数定义
x: # 参数名
dtype: tensor # 必填,数据类型
dim:
dtype: int
type_cast: tensor # 支持隐式类型转换
index:
dtype: tensor
returns: # 输出定义
output:
dtype: tensor
dispatch: # PyBoost分发配置
enable: True # 启用PyBoost代码生成
YAML字段说明:
| 字段 | 说明 | 示例 |
|---|---|---|
args |
输入参数定义 | x, dim, index |
dtype |
数据类型 | tensor, int, float, bool |
default |
默认值 | default: 0 |
type_cast |
类型转换支持 | type_cast: tensor |
prim_init |
是否作为Primitive的__init__参数 | prim_init: True |
returns |
输出定义 | output |
dispatch |
分发配置 | enable: True |
function |
函数配置 | disable: True |
class |
类配置 | name: NewName |
2.2.3 从YAML到代码生成的映射关系
YAML定义文件 (gather_d_op.yaml)
│
▼
┌───────────────────────────┐
│ OpProtoLoader.load() │
│ 解析YAML为OpProto对象 │
└───────────────────────────┘
│
▼
┌───────────────────────────┐
│ 各类Generator处理 │
│ - OpsDefCcGenerator │──► C++算子定义 (.cc/.h)
│ - OpsDefPyGenerator │──► Python算子定义 (.py)
│ - PyBoostGenerator │──► PyBoost代码
│ - ApiGenerator │──► API文档
└───────────────────────────┘
命名转换规则:
gather_d→ 函数名:gather_d,类名:GatherDgather_nd→ 函数名:gather_nd,类名:GatherNd
3. mint.gather接口实现分析
3.1 接口映射关系追踪
3.1.1 mint.gather接口定义位置
文件路径: mindspore/python/mindspore/mint/__init__.py
# 第21行
from mindspore.ops.function.array_func import gather_ext as gather
3.1.2 gather_ext函数定义
文件路径: mindspore/python/mindspore/ops/function/array_func.py
def gather_ext(input, dim, index):
r"""
Gather data from a tensor by indices.
.. math::
output[(i_0, i_1, ..., i_{dim}, i_{dim+1}, ..., i_n)] =
input[(i_0, i_1, ..., index[(i_0, i_1, ..., i_{dim}, i_{dim+1}, ..., i_n)], i_{dim+1}, ..., i_n)]
"""
return gather_d_op(input, dim, index)
3.1.3 底层算子原语
文件路径: mindspore/python/mindspore/ops/auto_generate/gen_ops_prim.py
class GatherD(Primitive):
r"""
.. code-block::
prim = ops.GatherD()
out = prim(x, dim, index)
is equivalent to
.. code-block::
ops.gather_d(x, dim, index)
"""
@prim_arg_register
def __init__(self):
pass
def __call__(self, x, dim, index):
res = pyboost_gather_d(self, [x, dim, index])
if not jit_context():
return res
if jit_context().compiled:
return jit_context().default_output()
return jit_context().run_op(self, res, x, dim, index)
gather_d_op = GatherD()
3.1.4 接口映射关系总结
mint.gather
│
│ (导入映射: from ... import gather_ext as gather)
▼
mindspore.ops.function.array_func.gather_ext
│
│ (函数调用: return gather_d_op(...))
▼
gather_d_op (GatherD算子实例)
│
│ (算子调用: pyboost_gather_d)
▼
pyboost_gather_d (底层实现)
结论:
mint.gather不是直接定义的,而是通过映射到其他ops接口实现- 映射链路:
mint.gather→gather_ext→gather_d_op(GatherD算子) - GatherD是一个自动生成的Primitive算子类
3.2 底层算子原语分析
3.2.1 算子原语名称
对应的Primitive算子原语名称为GatherD,该算子通过YAML配置自动生成。
3.2.2 算子原语定义
YAML定义 (gather_d_op.yaml):
gather_d:
args:
x:
dtype: tensor
dim:
dtype: int
type_cast: tensor
index:
dtype: tensor
returns:
output:
dtype: tensor
dispatch:
enable: True
3.2.3 算子原语与用户接口的关系
┌─────────────────────────────────────────────────────────────┐
│ 用户调用层 │
│ mint.gather(input, dim, index) │
│ ops.gather_d(input, dim, index) │
│ ops.GatherD()(input, dim, index) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ Python算子层 │
│ class GatherD(Primitive): │
│ def __call__(self, x, dim, index): │
│ return pyboost_gather_d(self, [x, dim, index]) │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ PyBoost加速层 │
│ pyboost_gather_d() │
│ - 参数校验 │
│ - 设备分发 │
│ - 内核调用 │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 底层内核实现 │
│ Ascend: ACLNN接口 │
│ GPU: CUDA内核 │
│ CPU: C++实现 │
└─────────────────────────────────────────────────────────────┘
4. MindSpore分布式算子核心机制详解
4.1 Info类模式概述
MindSpore的分布式算子实现采用Info类模式,每个算子都有对应的Info类负责:
- 布局推断(Layout Inference):推断输入/输出的分布式切分信息
- 策略推断(Strategy Inference):推断有效的并行策略
- 图替换(Graph Replacement):将原始节点替换为包含通信算子的子图(如需要)
Info类位置:
mindspore/ccsrc/frontend/parallel/ops_info/
├── operator_info.h # 基类定义
├── matmul_info.cc # MatMul分布式实现
├── add_info.cc # Add分布式实现
├── gatherd_info.cc # GatherD分布式实现
└── ...
4.2 Info类层次结构
4.2.1 基类:OperatorInfo
所有算子Info类都继承自OperatorInfo:
class OperatorInfo {
public:
// 分布式推断核心方法
virtual Status Infer(const OperatorParams &inputs, const OperatorParams &outputs) = 0;
virtual Status InferTensorInfo() = 0; // Layout流
virtual Status InferTensorMap() = 0; // Strategy流
virtual Status InferDevMatrixShape() = 0;
virtual Status InferMirrorOps() = 0;
// 图替换方法
virtual Status ReplaceNodeInputOrAttrs() override; // 输入替换
virtual Status replace_graph(const CNodePtr &cnode) override; // 子图替换
// 策略生成
virtual std::vector<StrategyPtr> GenerateStrategies(int32_t stage_id) = 0;
};
4.2.2 中间基类
部分算子有更具体的基类:
ArithmeticInfo(二元算子基类):
class ArithmeticInfo : public OperatorInfo {
protected:
// 二元算子通用逻辑
virtual Status InferTensorInfo() override;
virtual Status InferTensorMap() override;
};
// 继承此基类的算子
class AddInfo : public ArithmeticInfo {};
class SubInfo : public ArithmeticInfo {};
class MulInfo : public ArithmeticInfo {};
MatMulInfoBase(矩阵乘法基类):
class MatMulInfoBase : public OperatorInfo {
protected:
virtual Status InferMatrixDim() = 0;
virtual Status InferBiasTensorInfo() = 0;
};
class MatMulInfo : public MatMulInfoBase {};
class BatchMatMulInfo : public MatMulInfoBase {};
4.2.3 Info类层次图
OperatorInfo (基类)
├── ArithmeticInfo (二元算子)
│ ├── AddInfo
│ ├── SubInfo
│ ├── MulInfo
│ └── DivInfo
├── MatMulInfoBase (矩阵乘法)
│ ├── MatMulInfo
│ └── BatchMatMulInfo
├── ReduceInfo (归约算子)
│ └── ReduceSumInfo
├── GatherDInfo (Gather算子)
└── ... 其他基类
4.3 两种推断流程(核心)
MindSpore分布式算子有两种推断流程,这是理解分布式算子实现的关键:
4.3.1 Layout流(推荐)
特点:
- 使用
TensorInfo和TensorLayout作为抽象 - 推断更灵活,支持复杂切分模式
- 适用于所有算子
核心方法:
| 方法 | 描述 | 作用 |
|---|---|---|
InferTensorInfo() |
推断输入/输出TensorInfo |
Layout流的核心实现 |
InferOutputTensorInfo() |
Layout流中的输出推断 | 推断输出布局 |
InferInputTensorInfo() |
Layout流中的输入推断 | 输入验证 |
TensorInfo结构:
class TensorInfo {
public:
TensorLayout tensor_layout_; // 包含shape, tensor_map, dev_matrix_shape
TensorLayout GetTensorLayout() const;
Shape GetShape() const; // 张量的物理形状
TensorMap GetTensorMap() const; // 切分策略(-1表示不切分)
DevMatrixShape GetDevMatrixShape() const; // 设备矩阵形状
};
4.3.2 Strategy流(传统)
特点:
- 直接推断
tensor_map和dev_matrix_shape - 逻辑简单,但灵活性有限
- 多用于遗留算子
核心方法:
| 方法 | 描述 | 作用 |
|---|---|---|
InferTensorMap() |
推断输出tensor_map |
返回tensor_map |
InferDevMatrixShape() |
推断设备矩阵shape |
确定device_mesh |
GenerateStrategies() |
生成所有有效并行策略 | 策略枚举 |
4.3.3 两种流程对比
| 维度 | Layout流 | Strategy流 |
|---|---|---|
| 抽象层次 | 更高(TensorInfo/TensorLayout) | 较低(直接操作tensor_map) |
| 灵活性 | 更高,支持复杂切分模式 | 较低 |
| 适用场景 | 所有算子,特别是复杂算子 | 简单算子,遗留算子 |
| 推荐程度 | ✅ 推荐 | ⚠️ 传统方式 |
4.4 图替换机制(核心)
4.4.1 为什么需要图替换
当算子在分布式环境下需要额外的通信操作时,需要将原始节点替换为包含通信算子的子图。
常见场景:
- Reduce操作后需要聚合结果(如ReduceSum在特定切分下)
- 需要将局部结果广播到所有设备
- 需要在特定维度上通信(如AllReduce、AllGather)
4.4.2 图替换方法
| 方法 | 描述 | 调用时机 |
|---|---|---|
ReplaceNodeInputOrAttrs() |
替换输入张量或修改节点属性 | 编译优化阶段 |
replace_graph() |
将原始节点替换为通信算子子图 | 编译优化阶段 |
4.4.3 通信算子类型
| 通信算子 | 用途 | MindSpore对应 | 使用场景 |
|---|---|---|---|
| AllReduce | 归约(Sum/Max/Min/Prod) | ReplaceNode构造AllReduce |
需要全局归约时 |
| AllGather | 收集分片 | ReplaceNode构造AllGather |
需要收集所有分片时 |
| ReduceScatter | 归约后分发 | ReplaceNode构造ReduceScatter |
归约后需要切分输出时 |
4.5 输入验证机制
4.5.1 为什么需要输入验证
分布式算子必须验证输入Layout的兼容性,否则可能导致:
- 形状不匹配
- 切分策略冲突
- 不必要或错误的通信操作
4.5.2 验证逻辑位置
| 方法 | Layout流 | Strategy流 |
|---|---|---|
| 输入验证 | InferOutputTensorInfo()开头 |
InferTensorMap()开头 |
| 输出推断 | InferOutputTensorInfo()后半部分 |
InferTensorMap()后半部分 |
4.6 典型算子分析案例
4.6.1 案例1:MatMul(矩阵乘法)
算子基本信息:
- 算子名称:MatMul
- Info类:
MatMulInfo - 基类继承:
MatMulInfo → MatMulInfoBase → OperatorInfo
文件位置:mindspore/ccsrc/frontend/parallel/ops_info/matmul_info.cc
1. 输入验证逻辑:
参考源:InferMatrixDim(),文件位置:matmul_info.cc
验证规则:
- 输入
x和w对应维度必须兼容(矩阵乘法规则) - 考虑
transpose_a和transpose_b属性
2. Layout推断逻辑:
参考源:InferOutputTensorInfo(),文件位置:matmul_info.cc
推断规则(简化):
output_map[i] = x_map[i] (如果x在第i维切分)
output_map[j] = w_map[j] (如果w在第j维切分)
约束条件:
x在第k维的切分必须与w在第k维的切分匹配
(因为这两个维度需要做矩阵乘法内积累加)
3. 图替换逻辑:
参考源:replace_graph(),文件位置:matmul_info.cc
通信算子:
- 场景:当两个输入都在公共维度上切分时
- 算子:AllReduce(op=SUM)
- 原因:切分后的乘积需要在所有设备上求和
分布式MatMul计算流程:
场景:x形状[1024, 4096],w形状[4096, 512]
切分策略:x在dim=1切分,w在dim=0切分(都切4096维度)
┌─────────────────────────────────────────────────────────────┐
│ Rank 0: x[:, 0:2048], w[0:2048, :] │
│ Rank 1: x[:, 2048:4096], w[2048:4096, :] │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 每个Rank执行: │
│ local_result = MatMul(x_local, w_local) # [1024, 512] │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AllReduce(SUM) 聚合所有Rank的结果 │
│ output = AllReduce(local_result, SUM) │
└─────────────────────────────────────────────────────────────┘
4.6.2 案例2:Add(逐元素加法)
算子基本信息:
- 算子名称:Add
- Info类:
AddInfo - 基类继承:
AddInfo → ArithmeticInfo → OperatorInfo
文件位置:mindspore/ccsrc/frontend/parallel/ops_info/add_info.cc
ArithmeticInfo(二元算子基类):
class ArithmeticInfo : public OperatorInfo {
public:
// 二元算子通用实现
Status InferTensorInfo() override;
protected:
// 检查两个输入是否有相同Layout
Status CheckInputsTensorInfo();
};
1. 输入验证逻辑:
参考源:ArithmeticInfo::CheckInputsTensorInfo()
验证规则:
- 两个输入必须有相同的Layout
- 输出Layout与输入Layout相同
- 支持广播(当形状维度不足时自动扩展)
2. Layout推断逻辑:
参考源:ArithmeticInfo::InferTensorInfo()
推断规则:
output_tensor_map = input_tensor_map
output_shape = broadcast(x_shape, y_shape)
3. 图替换逻辑:
Add算子通常不需要图替换,因为:
- 逐元素操作不需要额外通信
- 两个输入在相同设备上操作
4.6.3 案例3:GatherD(索引收集)
算子基本信息:
- 算子名称:GatherD
- Info类:
GatherDInfo - 基类继承:
GatherDInfo → OperatorInfo
文件位置:mindspore/ccsrc/frontend/parallel/ops_info/gatherd_info.cc
1. GetAttrs实现 - 解析算子属性:
Status GatherDInfo::GetAttrs() {
if (input_value_.size() != 3) {
MS_LOG(ERROR) << name_ << ": Invalid input_value's size";
return FAILED;
}
if (!input_value_[1]->isa<Int64Imm>()) {
MS_LOG(ERROR) << name_ << ": The value of dim is not int";
return FAILED;
}
int64_t dim = GetValue<int64_t>(input_value_[1]);
int64_t input_dim = SizeToLong(inputs_shape_[0].size());
if ((dim > (input_dim - 1)) || (dim < -input_dim)) {
MS_LOG(ERROR) << name_ << ": The dim is out of range";
return FAILED;
}
if (dim < 0) {
dim += input_dim;
}
dim_ = LongToSize(dim);
return SUCCESS;
}
2. CheckStrategy实现 - 检查切分策略有效性:
Status GatherDInfo::CheckStrategy(const StrategyPtr &strategy) {
MS_EXCEPTION_IF_NULL(strategy);
if (CheckStrategyValue(strategy, inputs_shape_) != SUCCESS) {
return FAILED;
}
std::vector<Dimensions> stra = strategy->GetInputDim();
if (stra.size() != 2) {
MS_LOG(ERROR) << name_ << ": The size of strategy must be 2";
return FAILED;
}
const Dimensions &input_strategy = stra[0];
const Dimensions &index_strategy = stra[1];
if (input_strategy.size() != index_strategy.size()) {
MS_LOG(ERROR) << name_ << ": Dimension of X and Index strategy should be same";
return FAILED;
}
for (size_t i = 0; i < input_strategy.size(); i++) {
if (i != dim_) {
// 非dim维度:input和index切分策略必须相同
if (input_strategy[i] != index_strategy[i]) {
MS_LOG(ERROR) << name_ << ": Dimension " << i << " of X and Index should be same";
return FAILED;
}
} else {
// dim维度:index不能切分
if (index_strategy[i] != 1) {
MS_LOG(ERROR) << name_ << ": Index dimension dim can not be splited.";
return FAILED;
}
}
}
axis_shard_ = (input_strategy[dim_] != 1);
return SUCCESS;
}
3. InferTensorMap实现 - 推断张量映射:
Status GatherDInfo::InferTensorMap() {
// 构建输入张量映射
Shape input_tensor_map;
size_t size = inputs_shape_[0].size();
for (size_t i = 0; i < size; ++i) {
input_tensor_map.push_back(SizeToLong(size - i - 1));
}
// 构建index张量映射(dim维度不映射)
Shape index_tensor_map = input_tensor_map;
index_tensor_map[dim_] = -1; // -1表示该维度不切分
inputs_tensor_map_.push_back(input_tensor_map); // input
inputs_tensor_map_.push_back(index_tensor_map); // index
outputs_tensor_map_.push_back(index_tensor_map); // output
return SUCCESS;
}
张量映射说明:
tensor_map = [1, 0, -1]表示:- 张量第0维映射到设备第1维
- 张量第1维映射到设备第0维
- 张量第2维不切分(-1)
4. replace_graph实现 - 生成替换子图:
当在dim维度切分时,需要生成替换子图:
ReplaceGraphPtr GatherDInfo::replace_graph(const CNodePtr &cnode) {
if (!axis_shard_) {
return replace_graph_;
}
GenerateGraph gen_g = GenerateGraph(attrs_);
if (gen_g.Init(cnode) != SUCCESS) {
MS_LOG_WITH_NODE(EXCEPTION, cnode) << name_ << "GenerateGraph Init failed";
}
if (InferBias() != SUCCESS) {
MS_LOG_WITH_NODE(EXCEPTION, cnode) << name_ << ": Infer Bias failed.";
}
// 分布式GatherD实现逻辑:
// 1. 将x按dim维度切分成N段,每个rank持有一段
// 2. 每个rank持有完整的index
// 3. 计算局部索引:sub = index - bias
// 4. 处理越界索引:relu + minimum
// 5. 执行局部gatherd
// 6. 通过AllReduce聚合结果
auto sub = gen_g.PushBack({gen_g.NewOpInst(SUB), gen_g.virtual_input_node(), CreateInt32Tensor(bias_)});
auto relu = gen_g.PushBack({gen_g.NewOpInst(RELU), sub});
auto minimum = gen_g.PushBack({gen_g.NewOpInst(MINIMUM), relu, CreateInt32Tensor(slice_size_ - 1)});
auto equal = gen_g.PushBack({gen_g.NewOpInst(EQUAL), sub, minimum});
auto gatherd = gen_g.PushBack({gen_g.NewOpInst(GATHERD), gen_g.virtual_input_node(), CreatInt64Imm(dim_), minimum});
auto dtype = gen_g.PushBack({gen_g.NewOpInst(DTYPE), gatherd});
auto dtype_id = gen_g.PushBack({gen_g.NewOpInst(DTYPETOENUM), CreateStringImm("DtypeToEnum"),
CreateStringImm("dtype"), dtype});
auto cast = gen_g.PushBack({gen_g.NewOpInst(CAST), equal, dtype_id});
auto mul = gen_g.PushBack({gen_g.NewOpInst(MUL), gatherd, cast});
if (InferGroup() != SUCCESS) {
MS_LOG_WITH_NODE(EXCEPTION, cnode) << name_ << ": Infer Group failed.";
}
// 添加AllReduce通信算子
Attr attr_op = std::make_pair(OP, MakeValue(REDUCE_OP_SUM));
Attr attr_group = std::make_pair(GROUP, MakeValue(group_.name()));
OperatorAttrs attrs = {attr_op, attr_group};
AnfNodePtr reduce_op = gen_g.PushBack({gen_g.NewOpInst(ALL_REDUCE, attrs), mul});
std::vector<std::pair<AnfNodePtr, int64_t>> input_nodes = {
std::make_pair(sub, 3),
std::make_pair(gatherd, 1)
};
replace_graph_ = std::make_shared<std::pair<std::vector<std::pair<AnfNodePtr, int64_t>>, AnfNodePtr>>(
std::make_pair(input_nodes, reduce_op));
return replace_graph_;
}
分布式GatherD计算流程图:
原始计算: output = GatherD(x, dim, index)
当dim维度切分时:
┌─────────────────────────────────────────────────────────────┐
│ Rank 0: x_slice[0], index (完整) │
│ Rank 1: x_slice[1], index (完整) │
│ Rank 2: x_slice[2], index (完整) │
│ ... │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ 每个Rank执行: │
│ local_index = max(0, min(index - bias, slice_size - 1)) │
│ local_result = GatherD(x_slice, dim, local_index) │
│ mask = (index >= bias) & (index < bias + slice_size) │
│ result = local_result * mask │
└─────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────┐
│ AllReduce(SUM) 聚合所有Rank的结果 │
└─────────────────────────────────────────────────────────────┘
4.7 常见算子模式总结
4.7.1 逐元素二元算子(Add, Sub, Mul, Div)
特点:
- 两个输入必须有相同的Layout
- 输出Layout与输入Layout相同
- 通常不需要图替换
推断逻辑:
input1_layout == input2_layout
output_layout = input1_layout
4.7.2 矩阵乘法算子(MatMul, BatchMatMul)
特点:
- 支持不同切分策略(在不同矩阵维度上)
- 输出Layout从输入推断
- 可能需要AllReduce(如果需要求和)
推断逻辑:
output_map[i] = input_map1[i] (如果input_map1[i] != input_map2[k])
output_map[j] = input_map2[j] (其他情况)
4.7.3 归约算子(ReduceSum, ReduceMean)
特点:
- 在指定维度上归约
- 输出tensor_map在归约维度上为-1(不切分)
- 可能需要AllReduce / ReduceScatter
推断逻辑:
output_map[reduce_dim] = -1
output_map[other_dim] = input_map[other_dim]
4.8 分析模式总结
4.8.1 分析YAML定义
- 识别输入/输出参数
- 识别属性参数
- 理解算子的基本语义
4.8.2 定位Info类
# 查找Info类文件
find mindspore/ccsrc/frontend/parallel/ops_info/ -name "*{op_name}_info.cc"
4.8.3 分析Info类结构
- 继承关系(基类是什么)
- 重写了哪些方法
- 实现了哪些分布式逻辑
4.8.4 提取参考逻辑
| 步骤 | 目标 | 参考方法 |
|---|---|---|
| 1 | 输入验证逻辑 | CheckInputsTensorInfo() / 输入验证部分 |
| 2 | Layout推断逻辑 | InferTensorInfo() / InferOutputTensorInfo() / InferTensorMap() |
| 3 | 图替换逻辑 | replace_graph() / ReplaceNodeInputOrAttrs() |
5. 分布式算子实现参考
5.1 关键目录说明
| 目录 | 说明 |
|---|---|
mindspore/ops/op_def/yaml/ |
算子YAML定义 |
mindspore/ccsrc/frontend/parallel/ops_info/ |
分布式算子实现 |
mindspore/ccsrc/frontend/parallel/tensor_layout/ |
张量布局相关 |
tests/ut/cpp/parallel/ |
分布式算子单元测试 |
5.2 必要的前置知识
- C++编程:熟悉面向对象编程、智能指针、模板等
- 分布式计算基础:数据并行、模型并行、流水线并行概念
- 通信原语:AllReduce、AllGather、ReduceScatter等
- MindSpore框架:算子原语、计算图、自动微分
5.3 开发规范与最佳实践
5.3.1 代码风格与命名规范
命名规范:
- 类名:大驼峰命名,如
GatherDInfo - 函数名:大驼峰命名,如
InferTensorMap - 变量名:下划线命名,如
input_tensor_map - 成员变量:下划线结尾,如
dim_
代码风格:
- 遵循Google C++代码规范
- 使用智能指针管理内存
- 添加必要的注释
5.3.2 错误处理与日志打印
Status XxxInfo::CheckStrategy(const StrategyPtr &strategy) {
MS_EXCEPTION_IF_NULL(strategy); // 空指针检查
if (CheckStrategyValue(strategy, inputs_shape_) != SUCCESS) {
MS_LOG(ERROR) << name_ << ": Invalid strategy"; // 错误日志
return FAILED;
}
MS_LOG(INFO) << name_ << ": Strategy check passed"; // 信息日志
return SUCCESS;
}
5.3.3 测试用例编写规范
- 覆盖所有策略组合
- 测试边界条件
- 验证通信正确性
- 检查梯度计算
5.3.4 性能与内存优化建议
- 避免不必要的张量拷贝
- 使用引用传递
- 合理设置通信组
- 利用张量重分布机制
6. 总结与参考资源
6.1 本文档核心内容回顾
- 接口体系:理解MindSpore的NN接口、functional接口、Tensor接口、mint接口与算子原语的关系
- 代码生成:掌握YAML定义到Python/C++代码的自动生成流程
- mint.gather案例:完整追踪从用户接口到底层算子的调用链路
- 分布式实现:深入分析GatherDInfo的分布式策略实现机制
- 开发流程:系统掌握分布式算子开发的完整流程
6.2 分布式算子开发常用资源与文档链接
官方文档:
代码参考:
- 分布式算子基类:
mindspore/ccsrc/frontend/parallel/ops_info/operator_info.h - 策略定义:
mindspore/ccsrc/frontend/parallel/strategy.h - 张量布局:
mindspore/ccsrc/frontend/parallel/tensor_layout/tensor_layout.h
6.3 常见问题与解决方案参考
| 问题 | 解决方案 |
|---|---|
| 策略检查失败 | 检查CheckStrategy实现,确保切分策略符合算子语义 |
| 通信组创建失败 | 检查InferGroup实现,确保设备矩阵和张量映射正确 |
| 梯度计算错误 | 检查InferMirrorOps实现,确保镜像算子正确创建 |
| 性能不达预期 | 分析通信代价,考虑使用ReduceScatter等优化 |
6.4 进一步学习的建议与路径
-
入门阶段:
- 阅读现有分布式算子实现(如MatMul、LayerNorm)
- 运行单元测试,理解测试框架
-
进阶阶段:
- 实现一个简单的分布式算子(如ElementWise操作)
- 学习代价模型和策略选择机制
-
高级阶段:
- 实现复杂的分布式算子(如Attention、Convolution)
- 参与自动并行优化
附录:关键代码文件索引
| 文件 | 路径 | 说明 |
|---|---|---|
| mint接口定义 | mindspore/python/mindspore/mint/__init__.py |
mint模块入口 |
| gather_ext函数 | mindspore/python/mindspore/ops/function/array_func.py |
gather函数实现 |
| GatherD算子 | mindspore/python/mindspore/ops/auto_generate/gen_ops_prim.py |
自动生成的算子原语 |
| YAML定义 | mindspore/ops/op_def/yaml/gather_d_op.yaml |
算子YAML配置 |
| 代码生成脚本 | mindspore/python/mindspore/ops_generate/gen_ops.py |
代码生成主脚本 |
| 分布式算子基类 | mindspore/ccsrc/frontend/parallel/ops_info/operator_info.h |
OperatorInfo定义 |
| GatherDInfo头文件 | mindspore/ccsrc/frontend/parallel/ops_info/gatherd_info.h |
GatherD分布式算子声明 |
| GatherDInfo实现 | mindspore/ccsrc/frontend/parallel/ops_info/gatherd_info.cc |
GatherD分布式算子实现 |
| 策略类 | mindspore/ccsrc/frontend/parallel/strategy.h |
Strategy类定义 |
| 张量布局 | mindspore/ccsrc/frontend/parallel/tensor_layout/tensor_layout.h |
TensorLayout类定义 |