文档首页 > > 推理场景> 应用软件开发指南 (C&C++)> 开发典型功能点的介绍> 单算子调用> 未被封装成AscendCL接口的算子>

动态Shape算子(注册算子选择器)

动态Shape算子(注册算子选择器)

分享
更新时间:2021/01/27 GMT+08:00

在加载与执行动态Shape算子前,您需要参见TBE自定义算子开发指南中的“TIK自定义算子动态Shape专题”中的说明开发自定义算子以及生成对应的二进制文件。

基本原理

动态Shape场景下,算子加载与执行的流程如下:

  1. 资源初始化,包括AscendCL初始化、设置单算子模型文件的加载目录、指定用于运算的Device等。
    • 调用aclInit接口实现AscendCL初始化。
    • 调用AscendCL接口注册要编译的自定义算子:
      • 调用aclopRegisterCompileFunc接口注册算子选择器(即选择Tiling策略的函数),用于在算子执行时,能针对不同Shape,选择相应的Tiling策略。

        算子选择器需由用户提前定义并实现:

        • 函数原型:
          typedef aclError (*aclopCompileFunc)(int numInputs, const aclTensorDesc *const inputDesc[], int numOutputs, const aclTensorDesc *const outputDesc[], const aclopAttr *opAttr, aclopKernelDesc *aclopKernelDesc);
        • 函数实现:

          用户自行编写代码逻辑实现Tiling策略选择、Tiling参数生成,并将调用aclopSetKernelArgs接口,设置算子Tiling参数、执行并发数等。

      • 调用aclopCreateKernel接口将算子注册到系统内部,用于在算子执行时,查找到算子实现代码。
    • 调用aclrtSetDevice接口指定运算的Device。
    • 调用aclrtCreateContext接口显式创建一个Context,调用aclrtCreateStream接口显式创建一个Stream。

      若没有显式创建Stream,则使用默认Stream,默认Stream是在调用aclrtSetDevice接口时隐式创建的,默认Stream作为接口入参时,直接传NULL。

  2. 用户自行构造算子描述信息(输入输出Tensor描述、算子属性等)、申请存放算子输入输出数据的内存。
  3. 将算子输入数据从Host复制到Device上。
    • 调用aclrtMemcpy接口实现同步内存复制,内存使用结束后需及时释放。
    • 调用aclrtMemcpyAsync接口实现异步内存复制,内存使用结束后需及时释放。
  4. 编译单算子。

    调用aclopUpdateParams接口编译指定算子,触发算子选择器的调用逻辑。

  5. 执行单算子。

    调用aclopExecute接口加载并执行算子。

  6. 将算子运算的输出数据从Device上复制到Host上(提前申请Host上的内存)。
    • 调用aclrtMemcpy接口实现同步内存复制,内存使用结束后需及时释放。
    • 调用aclrtMemcpyAsync接口实现异步内存复制,内存使用结束后需及时释放。
  7. 按顺序先释放Stream资源,再释放Context资源,最后释放Device资源。
  8. 调用aclFinalize接口实现AscendCL去初始化。

示例代码

示例代码如下,您可以从“Acllib组件的安装目录/acllib/sample/acl_execute_op/acl_execute_batchnorm/src”目录下查看完整的样例代码,在样例代码中,调用接口后,增加了异常处理的分支,同时通过ERROR_LOG记录报错日志、通过INFO_LOG记录各动作的提示日志,示例代码中不一一列举。

以下是关键步骤的代码示例,不可以直接拷贝编译运行,仅供参考。

 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
#include "acl/acl.h"
//......

//1.资源初始化
//此处是相对路径,相对可执行文件所在的目录
aclError ret = aclInit(NULL);
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_NORMAL_ONLY);
aclrtMalloc(&devInput_gamma, size_gamma, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtMalloc(&devInput_beta, size_beta, ACL_MEM_MALLOC_NORMAL_ONLY);
aclrtMalloc(&devOutput, size_output, ACL_MEM_MALLOC_NORMAL_ONLY);

//3.将算子输入数据从Host复制到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.调用aclopExecute接口加载并执行算子
aclopExecute("BatchNorm", 3, input_desc, inputs, 1, output_desc, outputs, nullptr, stream);

//-----自定义函数BatchNormTest(n, c, h, w),执行以上操作-----

//6.将算子运算的输出数据从Device上复制到Host上(提前申请Host上的内存)
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 释放Host上的内存
delete[]input;
delete[]gamma;
delete[]beta;
delete[]output;
//7.3 释放Device上的内存
aclrtFree(devInput);
aclrtFree(devInput_gamma);
aclrtFree(devInput_beta));
aclrtFree(devOutput);
//7.4 依次释放Stream、Context、Device资源,如果未显式创建Stream、Context,则无需释放
aclrtDestroyStream(stream);
aclrtDestroyContext(context);
aclrtResetDevice(deviceId);
aclFinalize();
分享:

    相关文档

    相关产品

文档是否有解决您的问题?

提交成功!非常感谢您的反馈,我们会继续努力做到更好!
反馈提交失败,请稍后再试!

*必选

请至少选择或填写一项反馈信息

字符长度不能超过200

提交反馈 取消

如您有其它疑问,您也可以通过华为云社区论坛频道来与我们联系探讨

智能客服提问云社区提问