算子原语通常包括:
- 算子名:算子名用于唯一标识个算子。
- 输入:算子输入Tensor。
- 属性:一般描述算法参数
- 输入数据合法性校验:对输入数据、属性进行合法性校验
- 输出数据类型和维度推导:用于推导输出的数据类型和维度。
自定义算子
- 路径mindspore/python/mindspore/ops/operations
- TensorAddV2继承于PrimitiveWithInfer。
# mindspore/python/mindspore/ops/operations/math_ops.py
class TensorAddV2(PrimitiveWithInfer):
"""
Adds two input tensors element-wise.
"""
@prim_attr_register
def __init__(self):
self.init_prim_io_names(inputs=['x1', 'x2'], outputs=['y'])
def infer_shape(self, x1_shape, x2_shape):
validator.check_integer('input dims', len(x1_shape), len(x2_shape), Rel.EQ, self.name)
for i in range(len(x1_shape)):
validator.check_integer('input_shape', x1_shape[i], x2_shape[i], Rel.EQ, self.name)
return x1_shape
def infer_dtype(self, x1_dtype, x2_type):
validator.check_tensor_type_same({'x1_dtype': x1_dtype}, [mstype.float32], self.name)
validator.check_tensor_type_same({'x2_dtype': x2_dtype}, [mstype.float32], self.name)
return x1_dtype
在__init__.py中导出TensorAddV2类型
# mindspore/python/mindspore/ops/operations/__init__.py from .math_ops import (Abs, ACos, ..., TensorAddV2) ... __all__ = [ 'ReverseSequence', 'CropAndResize', ..., 'TensorAddV2' ](2)实现GPU算子
- GPU自定义算子继承于GPUKernel:
// mindspore/ccsrc/backend/kernel_compiler/gpu/math/tensor_add_v2_gpu_kernel.h templateclass TensorAddV2GpuKernel : public GpuKernel { public: TensorAddV2GpuKernel() : element_num_(1) {} ~TensorAddV2GpuKernel() override = default; bool Init(const CNodePtr &kernel_node) override { auto shape = AnfAlgo::GetPrevNodeOutputInferShape(kernel_node, 0); for (size_t i = 0; i < shape.size(); i++) { element_num_ *= shape[i]; } InitSizeLists(); return true; } const std::vector &GetInputSizeList() const override { return input_size_list_; } const std::vector &GetOutputSizeList() const override { return output_size_list_; } const std::vector &GetWorkspaceSizeList() const override { return workspace_size_list_; } bool Launch(const std::vector &inputs, const std::vector &, const std::vector &outputs, void *stream_ptr) override { T *x1 = GetDeviceAddress (inputs, 0); T *x2 = GetDeviceAddress (inputs, 1); T *y = GetDeviceAddress (outputs, 0); TensorAddV2(element_num_, x1, x2, y, reinterpret_cast (stream_ptr)); return true; } protected: void InitSizeLists() override { input_size_list_.push_back(element_num_ * sizeof(T)); input_size_list_.push_back(element_num_ * sizeof(T)); output_size_list_.push_back(element_num_ * sizeof(T)); } private: size_t element_num_; std::vector input_size_list_; std::vector output_size_list_; std::vector workspace_size_list_; };
TensorAddV2中调用了CUDA kernel TensorAddV2Kernel来实现element_num个元素的并行相加:
// mindspore/ccsrc/backend/kernel_compiler/gpu/math/tensor_add_v2_gpu_kernel.h template(3)注册算子__global__ void TensorAddV2Kernel(const size_t element_num, const T* x1, const T* x2, T* y) { for (size_t i = blockIdx.x * blockDim.x + threadIdx.x; i < element_num; i += blockDim.x * gridDim.x) { y[i] = x1[i] + x2[i]; } } template void TensorAddV2(const size_t &element_num, const T* x1, const T* x2, T* y, cudaStream_t stream){ size_t thread_per_block = 256; size_t block_per_grid = (element_num + thread_per_block - 1 ) / thread_per_block; TensorAddV2Kernel<< >>(element_num, x1, x2, y); return; } template void TensorAddV2(const size_t &element_num, const float* x1, const float* x2, float* y, cudaStream_t stream);
- 算子信息包含:Primive、Input dtype, output dtype、GPU Kernel class、CUDA内置数据类型
- 注册支持float和int的TensorAddV2算子
// mindspore/ccsrc/backend/kernel_compiler/gpu/math/tensor_add_v2_gpu_kernel.cc
MS_REG_GPU_KERNEL_ONE(TensorAddV2, KernelAttr()
.AddInputAttr(kNumberTypeFloat32)
.AddInputAttr(kNumberTypeFloat32)
.AddOutputAttr(kNumberTypeFloat32),
TensorAddV2GpuKernel, float)
MS_REG_GPU_KERNEL_ONE(TensorAddV2, KernelAttr()
.AddInputAttr(kNumberTypeInt32)
.AddInputAttr(kNumberTypeInt32)
.AddOutputAttr(kNumberTypeInt32),
TensorAddV2GpuKernel, int)
(4)编译mindspore
- 进入mindspore根目录,执行编译
cd mindspore source activate py39_ms17 bash build.sh -e gpu -S on(5)算子验证
# tests/st/ops/gpu/test_tensoraddv2_op.py
import mindspore.context as context
from mindspore import Tensor
import mindspore.ops as ops
context.set_context(device_target='GPU')
@pytest.mark.level0
@pytest.mark.platform_x86_gpu_training
@pytest.mark.env_onecard
def test_TensorAdd():
x1 = Tensor(np.ones((3, 4), np.float32))
x2 = Tensor(np.ones((3, 4), np.float32))
y = ops.TensorAddV2()(x1, x2)
print('result: ', y)
输出
result: [[2. 2. 2. 2.] [2. 2. 2. 2.] [2. 2. 2. 2.]]



