Ascend C Sinh算子开发——香橙派310B
- 开发算子名称:Sinh
- 开发环境:香橙派AIPro
- soc_Version:310B4
- CANN安装目录:
/usr/local/Ascend/ascend-toolkit/latest
建议首先查看过程注意点
Sinh算子分析
首先来看Sinh算子要实现的功能:
sinh(x) = (exp(x) - exp(-x)) / 2.0
可以看到仅处理一个输入,获取一个输出,并且没有任何属性。那么我们可以参考samples仓中的AddCustom实现进行部分修改即可或者使用msopgen工具生成算子工程文件。
Sinh算子实现
生成算子工程
修改配置文件
修改文件CMakePresets.json编译配置项中的ASCEND_CANN_PACKAGE_PATH为/usr/local/Ascend/ascend-toolkit/latest。
op_host/sinh_custom_tiling.h
op_host/sinh_custom.cpp
op_kernel/sinh_custom.cpp
算子编译部署
算子测试
使用samples仓中AddCustom算子下的AclNNInvocation工程经过修改之后,进行测试。
scripts/gen_data.py
src/main.cpp
src/op_runner.cpp
- 对部署的算子进行测试
- 测试结果

过程注意点
- 在使用
msopgen工具(mindspore op generator)生成算子工程时,soc_version要设置为Ascend310B,而不是Ascend310B4。 官方文档-msopgen生成自定义算子工程 - 算子工程不要通过
cp复制,需要使用msopgen根据json文件生成。 - 初始化生成的算子工程,需要修改文件
CMakePresets.json编译配置项中的ASCEND_CANN_PACKAGE_PATH为CANN软件包安装后的实际路径。 - SinhCustom算子配置文件内容如下
Ascend C Sinh算子开发——香橙派310B
/usr/local/Ascend/ascend-toolkit/latestSinh算子分析
首先来看Sinh算子要实现的功能:
sinh(x) = (exp(x) - exp(-x)) / 2.0可以看到仅处理一个输入,获取一个输出,并且没有任何属性。那么我们可以参考
samples仓中的AddCustom实现进行部分修改即可或者使用msopgen工具生成算子工程文件。Sinh算子实现
生成算子工程
修改配置文件
修改文件
CMakePresets.json编译配置项中的ASCEND_CANN_PACKAGE_PATH为/usr/local/Ascend/ascend-toolkit/latest。"ASCEND_CANN_PACKAGE_PATH": { "type": "PATH", "value": "/usr/local/Ascend/ascend-toolkit/latest" }op_host/sinh_custom_tiling.h#include "register/tilingdata_base.h" namespace optiling { BEGIN_TILING_DATA_DEF(SinhCustomTilingData) TILING_DATA_FIELD_DEF(uint32_t, totalLength); TILING_DATA_FIELD_DEF(uint32_t, tileNum); END_TILING_DATA_DEF; REGISTER_TILING_DATA_CLASS(SinhCustom, SinhCustomTilingData) }op_host/sinh_custom.cpp#include "sinh_custom_tiling.h" #include "register/op_def_registry.h" namespace optiling { const uint32_t BLOCK_DIM = 8; const uint32_t TILE_NUM = 8; static ge::graphStatus TilingFunc(gert::TilingContext* context) { SinhCustomTilingData tiling; uint32_t totalLength = context->GetInputTensor(0)->GetShapeSize(); context->SetBlockDim(BLOCK_DIM); tiling.set_totalLength(totalLength); tiling.set_tileNum(TILE_NUM); tiling.SaveToBuffer(context->GetRawTilingData()->GetData(), context->GetRawTilingData()->GetCapacity()); context->GetRawTilingData()->SetDataSize(tiling.GetDataSize()); size_t *currentWorkspace = context->GetWorkspaceSizes(1); currentWorkspace[0] = 0; return ge::GRAPH_SUCCESS; } }op_kernel/sinh_custom.cpp#include "kernel_operator.h" using namespace AscendC; constexpr int32_t BUFFER_NUM = 2; class KernelSinh { public: __aicore__ inline KernelSinh() {} __aicore__ inline void Init(GM_ADDR x, GM_ADDR y, uint32_t totalLength, uint32_t tileNum) { ASSERT(GetBlockNum() != 0 && "block dim can not be zero!"); this->blockLength = totalLength / GetBlockNum(); this->tileNum = tileNum; ASSERT(tileNum != 0 && "tile num can not be zero!"); this->tileLength = this->blockLength / tileNum / BUFFER_NUM; xGm.SetGlobalBuffer((__gm__ half*)x + this->blockLength * GetBlockIdx(), this->blockLength); yGm.SetGlobalBuffer((__gm__ half*)y + this->blockLength * GetBlockIdx(), this->blockLength); pipe.InitBuffer(inQueueX, BUFFER_NUM, this->tileLength * sizeof(half)); pipe.InitBuffer(outQueueY, BUFFER_NUM, this->tileLength * sizeof(half)); } __aicore__ inline void Process() { int32_t loopCount = this->tileNum * BUFFER_NUM; for (int32_t i = 0; i < loopCount; i++) { CopyIn(i); Compute(i); CopyOut(i); } } private: __aicore__ inline void CopyIn(int32_t progress) { LocalTensor<half> xLocal = inQueueX.AllocTensor<half>(); DataCopy(xLocal, xGm[progress * tileLength], tileLength); inQueueX.EnQue(xLocal); } __aicore__ inline void Compute(int32_t progress) { LocalTensor<half> xLocal = inQueueX.DeQue<half>(); LocalTensor<half> yLocal = outQueueY.AllocTensor<half>(); Exp(xLocal, xLocal, this->tileLength); // x = e^{x} Reciprocal(yLocal, xLocal, this->tileLength); // y = e^{-x} Sub(yLocal, xLocal, yLocal, this->tileLength); // x = x - y = e^{x} - e^{-x}; half scalar = 0.5; Muls(yLocal, yLocal, (half)0.5, this->tileLength); // y = x * 0.5 = (e^{x} - e^{-x}) * 0.5; outQueueY.EnQue<half>(yLocal); inQueueX.FreeTensor(xLocal); } __aicore__ inline void CopyOut(int32_t progress) { LocalTensor<half> yLocal = outQueueY.DeQue<half>(); DataCopy(yGm[progress * tileLength], yLocal, tileLength); outQueueY.FreeTensor(yLocal); } private: TPipe pipe; TQue<QuePosition::VECIN, BUFFER_NUM> inQueueX; TQue<QuePosition::VECOUT, BUFFER_NUM> outQueueY; GlobalTensor<half> xGm; GlobalTensor<half> yGm; uint32_t blockLength; uint32_t tileNum; uint32_t tileLength; }; extern "C" __global__ __aicore__ void sinh_custom(GM_ADDR x, GM_ADDR y, GM_ADDR workspace, GM_ADDR tiling) { GET_TILING_DATA(tiling_data, tiling); KernelSinh op; op.Init(x, y, tiling_data.totalLength, tiling_data.tileNum); op.Process(); }算子编译部署
算子测试
使用
samples仓中AddCustom算子下的AclNNInvocation工程经过修改之后,进行测试。scripts/gen_data.py#!/usr/bin/python3 # -*- coding:utf-8 -*- # Copyright 2022-2023 Huawei Technologies Co., Ltd import numpy as np def gen_golden_data_simple(): input_x = np.random.uniform(1, 10, [8, 2048]).astype(np.float16) golden = np.sinh(input_x) input_x.tofile("./input/input_x.bin") golden.tofile("./output/golden.bin") if __name__ == "__main__": gen_golden_data_simple()src/main.cppOperatorDesc CreateOpDesc() { // define operator std::vector<int64_t> shape { 8, 2048 }; aclDataType dataType = ACL_FLOAT16; aclFormat format = ACL_FORMAT_ND; OperatorDesc opDesc; opDesc.AddInputTensorDesc(dataType, shape.size(), shape.data(), format); opDesc.AddOutputTensorDesc(dataType, shape.size(), shape.data(), format); return opDesc; } bool SetInputData(OpRunner &runner) { size_t fileSize = 0; ReadFile("../input/input_x.bin", fileSize, runner.GetInputBuffer<void>(0), runner.GetInputSize(0)); INFO_LOG("Set input success"); return true; } bool ProcessOutputData(OpRunner &runner) { WriteFile("../output/output_z.bin", runner.GetOutputBuffer<void>(0), runner.GetOutputSize(0)); INFO_LOG("Write output success"); return true; }src/op_runner.cpp/** * @file op_runner.cpp * * Copyright (C) 2020. Huawei Technologies Co., Ltd. All rights reserved. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ #include "op_runner.h" #include "aclnn_sinh_custom.h" #include <limits> #include <cassert> #include "acl/acl_op_compiler.h" #include "common.h" bool OpRunner::RunOp() { for (size_t i = 0; i < numInputs_; ++i) { auto size = GetInputSize(i); aclrtMemcpyKind kind = ACL_MEMCPY_HOST_TO_DEVICE; if (g_isDevice) { kind = ACL_MEMCPY_DEVICE_TO_DEVICE; } if (aclrtMemcpy(devInputs_[i], size, hostInputs_[i], size, kind) != ACL_SUCCESS) { ERROR_LOG("Copy input[%zu] failed", i); return false; } INFO_LOG("Copy input[%zu] success", i); } aclrtStream stream = nullptr; if (aclrtCreateStream(&stream) != ACL_SUCCESS) { ERROR_LOG("Create stream failed"); return false; } INFO_LOG("Create stream success"); size_t workspaceSize = 0; aclOpExecutor *handle = nullptr; //添加计算workspace大小并申请内存代码 auto ret = aclnnSinhCustomGetWorkspaceSize(inputTensor_[0], outputTensor_[0], &workspaceSize, &handle); if (ret != ACL_SUCCESS) { (void)aclrtDestroyStream(stream); ERROR_LOG("Get Operator Workspace failed. error code is %d", static_cast<int32_t>(ret)); return false; } void *workspace = nullptr; if (workspaceSize != 0) { if (aclrtMalloc(&workspace, workspaceSize, ACL_MEM_MALLOC_NORMAL_ONLY) != ACL_SUCCESS) { ERROR_LOG("Malloc device memory failed"); } } //添加执行算子代码 if (aclnnSinhCustom(workspace, workspaceSize, handle, stream) != ACL_SUCCESS) { (void)aclrtDestroyStream(stream); ERROR_LOG("Execute Operator failed. error code is %d", static_cast<int32_t>(ret)); return false; } ret = aclrtSynchronizeStreamWithTimeout(stream, 5000); if (ret != SUCCESS) { ERROR_LOG("Synchronize stream failed. error code is %d", static_cast<int32_t>(ret)); (void)aclrtDestroyStream(stream); return false; } INFO_LOG("Synchronize stream success"); for (size_t i = 0; i < numOutputs_; ++i) { auto size = GetOutputSize(i); aclrtMemcpyKind kind = ACL_MEMCPY_DEVICE_TO_HOST; if (g_isDevice) { kind = ACL_MEMCPY_DEVICE_TO_DEVICE; } if (aclrtMemcpy(hostOutputs_[i], size, devOutputs_[i], size, kind) != ACL_SUCCESS) { INFO_LOG("Copy output[%zu] success", i); (void)aclrtDestroyStream(stream); return false; } INFO_LOG("Copy output[%zu] success", i); } (void)aclrtDestroyStream(stream); return true; }过程注意点
msopgen工具(mindspore op generator)生成算子工程时,soc_version要设置为Ascend310B,而不是Ascend310B4。${INSTALL_DIR}/python/site-packages/bin/msopgen gen -i sinh.json -c ai_core-Ascend310B -lan cpp -out Sinhcp复制,需要使用msopgen根据json文件生成。CMakePresets.json编译配置项中的ASCEND_CANN_PACKAGE_PATH为CANN软件包安装后的实际路径。[ { "op": "SinhCustom", "language":"cpp", "input_desc": [ { "name": "x", "param_type": "required", "format": [ "ND" ], "type": [ "fp16" ] } ], "output_desc": [ { "name": "y", "param_type": "required", "format": [ "ND" ], "type": [ "fp16" ] } ] } ]