文档
注册

执行动态Shape算子示例代码(注册算子选择器)

本节介绍基于单算子模型执行的方式调用动态Shape算子的关键接口、示例代码,注册算子选择器的使用场景有限,主要针对TIK自定义算子。

前提条件

在加载与执行动态Shape算子前,您需要参见TBE&AI CPU算子开发指南中的“专题 > 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。
    • 调用aclrtCreateStream接口显式创建一个Stream。

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

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

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

  5. 执行单算子。

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

  6. 获取算子运算的输出数据。
    • 调用aclrtMemcpy接口实现同步内存复制,内存使用结束后需及时释放。
    • 调用aclrtMemcpyAsync接口实现异步内存复制,内存使用结束后需及时释放。
  7. 按顺序先释放Stream资源,再释放Context资源,最后释放Device资源。
  8. 调用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_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.将算子输入数据复制到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();
搜索结果
找到“0”个结果

当前产品无相关内容

未找到相关内容,请尝试其他搜索词