执行动态Shape算子示例代码(注册算子选择器)
本节介绍基于单算子模型执行的方式调用动态Shape算子的关键接口、示例代码,注册算子选择器的使用场景有限,主要针对TIK自定义算子。
基本原理
动态Shape、注册算子选择器场景下,算子加载与执行的流程如下:
- 资源初始化,包括AscendCL初始化、设置单算子模型文件的加载目录、指定用于运算的Device等。
- 调用aclInit接口实现AscendCL初始化。
- 调用AscendCL接口注册要编译的自定义算子:
- 调用aclrtSetDevice接口指定运算的Device。
- 调用aclrtCreateStream接口显式创建一个Stream。
若没有显式创建Stream,则使用默认Stream,默认Stream是在调用aclrtSetDevice接口时隐式创建的,默认Stream作为接口入参时,直接传NULL。
- 用户自行构造算子描述信息(输入输出Tensor描述、算子属性等)、申请存放算子输入输出数据的内存。
- 将算子输入数据复制到Device上。
- 编译单算子。
- 执行单算子。
- 获取算子运算的输出数据。
- 按顺序先释放Stream资源,再释放Context资源,最后释放Device资源。
- 调用aclFinalize接口实现AscendCL去初始化。
示例代码
在样例代码中,调用接口后,需增加异常处理的分支,并记录报错日志、提示日志,此处不一一列举。以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。
动态Shape算子(注册算子选择器)样例的获取、编译运行请参见《TBE&AI CPU自定义算子开发指南》中的“专题 > TIK自定义算子动态Shape专题 > 样例使用”。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
#include "acl/acl.h" // ...... // 1.资源初始化 // 此处是相对路径,相对可执行文件所在的目录 aclError ret = aclInit(NULL); // 指定运算的Device int32_t deviceId=0 ; ret = aclrtSetDevice(deviceId); // 显式创建一个Stream // 用于维护一些异步操作的执行顺序,确保按照应用程序中的代码调用顺序执行任务 aclrtStream stream; ret = aclrtCreateStream(&stream); aclopRegisterCompileFunc("BatchNorm", SelectAclopBatchNorm); // 将算子Kernel的*.o文件需要用户提前编译好,并调用用户自定义函数该文件加载到内存buffer中,length表示内存大小,如果有多个算子Kernel的*.o文件,需要多次调用该接口 aclopCreateKernel("BatchNorm", "tiling_mode_1__kernel0", "tiling_mode_1__kernel0", buffer, length, ACL_ENGINE_AICORE, Deallocator); // -----自定义函数BatchNormTest(n, c, h, w),执行以下操作----- // 2.构造BatchNorm算子的输入输出Tensor、输入输出Tensor描述,并申请存放算子输入数据、输出数据的内存 aclTensorDesc *input_desc[3]; aclTensorDesc *output_desc[1]; input_desc[0] = aclCreateTensorDesc(ACL_FLOAT16, 4, shape_input, ACL_FORMAT_NCHW); input_desc[1] = aclCreateTensorDesc(ACL_FLOAT16, 1, shape_gamma, ACL_FORMAT_ND); input_desc[2] = aclCreateTensorDesc(ACL_FLOAT16, 1, shape_beta, ACL_FORMAT_ND); output_desc[0] = aclCreateTensorDesc(ACL_FLOAT16, 4, shape_out, ACL_FORMAT_NCHW); for (int i = 0; i < n * c * h * w; ++i) { input[i] = aclFloatToFloat16(1.0f); } for (int i = 0; i < c; ++i) { gamma[i] = aclFloatToFloat16(0.5f); beta[i] = aclFloatToFloat16(0.1f); } aclrtMalloc(&devInput, size_input, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(&devInput_gamma, size_gamma, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(&devInput_beta, size_beta, ACL_MEM_MALLOC_HUGE_FIRST); aclrtMalloc(&devOutput, size_output, ACL_MEM_MALLOC_HUGE_FIRST); // 3.将算子输入数据复制到Device上 aclrtMemcpy(devInput, size_input, input, size_input, ACL_MEMCPY_HOST_TO_DEVICE); aclrtMemcpy(devInput_gamma, size_gamma, gamma, size_gamma, ACL_MEMCPY_HOST_TO_DEVICE); aclrtMemcpy(devInput_beta, size_beta, beta, size_beta, ACL_MEMCPY_HOST_TO_DEVICE); // 4.调用aclopUpdateParams接口编译算子 aclopUpdateParams("BatchNorm", 3, input_desc, 1, output_desc, nullptr, ACL_ENGINE_AICORE, ACL_COMPILE_UNREGISTERED, nullptr)); // 5.调用aclopExecuteV2接口加载并执行算子 aclopExecuteV2("BatchNorm", 3, input_desc, inputs, 1, output_desc, outputs, nullptr, stream); // -----自定义函数BatchNormTest(n, c, h, w),执行以上操作----- // 6.获取算子运算的输出数据 aclrtMemcpy(output, size_output, devOutput, size_output, ACL_MEMCPY_DEVICE_TO_HOST); // 7.按顺序释放资源 // 7.1 释放算子的输入输出Tensor描述 for (auto desc : input_desc) { aclDestroyTensorDesc(desc); } for (auto desc : output_desc) { aclDestroyTensorDesc(desc); } // 7.2 及时释放不使用的内存 delete[]input; delete[]gamma; delete[]beta; delete[]output; // 7.3 释放Device上的内存 aclrtFree(devInput); aclrtFree(devInput_gamma); aclrtFree(devInput_beta)); aclrtFree(devOutput); // 7.4 依次释放Stream、Device资源,如果未显式创建Stream,则无需释放 aclrtDestroyStream(stream); aclrtResetDevice(deviceId); aclFinalize(); |
父主题: 单算子模型执行