单算子应用
概述
CANN Kit提供独立的算子创建和计算通路,三方框架可以在模型加载、推理过程中,将卷积、深度卷积等算子通过单算子对接的方式迁移至NPU,经过硬件平台的加速计算,与整网模式对比灵活度更高,相比于整网CPU计算性能更优。

以下为单算子Tensor创建,单算子执行器创建、加载、执行接口,接口使用请参见开发步骤。如要使用更丰富的设置和查询接口,请参见API参考。
表1 单算子接口及功能介绍
| 接口名 | 描述 |
|---|---|
| HiAI_SingleOpTensorDesc * HMS_HiAISingleOpTensorDesc_Create (const int64_t *dims, size_t dimNum, HiAI_SingleOpDataType dataType, HiAI_SingleOpFormat format, bool isVirtual); | 创建HiAI_SingleOpTensorDesc对象。 |
| void HMS_HiAISingleOpTensorDesc_Destroy (HiAI_SingleOpTensorDesc **tensorDesc); | 释放HiAI_SingleOpTensorDesc对象。 |
| HiAI_SingleOpBuffer * HMS_HiAISingleOpBuffer_Create (size_t dataSize); | 按照指定的内存大小创建HiAI_SingleOpBuffer对象。 |
| size_t HMS_HiAISingleOpBuffer_GetSize (const HiAI_SingleOpBuffer *buffer); | 查询HiAI_SingleOpBuffer的字节大小。 |
| void * HMS_HiAISingleOpBuffer_GetData (const HiAI_SingleOpBuffer *buffer); | 查询HiAI_SingleOpBuffer的内存地址。 |
| OH_NN_ReturnCode HMS_HiAISingleOpBuffer_Destroy (HiAI_SingleOpBuffer **buffer); | 释放HiAI_SingleOpBuffer对象。 |
| HiAI_SingleOpTensor * HMS_HiAISingleOpTensor_CreateFromTensorDesc (const HiAI_SingleOpTensorDesc *desc); | 根据HiAI_SingleOpTensorDesc创建HiAI_SingleOpTensor对象。 |
| HiAI_SingleOpTensor * HMS_HiAISingleOpTensor_CreateFromConst (const HiAI_SingleOpTensorDesc *desc, void *data, size_t dataSize); | 根据HiAI_SingleOpTensorDesc、常量数据(如卷积权重、偏置等)的内存地址和数据大小创建HiAI_SingleOpTensor对象。 |
| HiAI_SingleOpTensorDesc * HMS_HiAISingleOpTensor_GetTensorDesc (const HiAI_SingleOpTensor *tensor); | 获取HiAI_SingleOpTensor的Tensor描述。 |
| HiAI_SingleOpBuffer * HMS_HiAISingleOpTensor_GetBuffer (const HiAI_SingleOpTensor *tensor); | 获取HiAI_SingleOpTensor的Buffer。 |
| OH_NN_ReturnCode HMS_HiAISingleOpTensor_Destroy (HiAI_SingleOpTensor **tensor); | 释放HiAI_SingleOpTensor对象。 |
| HiAI_SingleOpOptions * HMS_HiAISingleOpOptions_Create (void); | 创建HiAI_SingleOpOptions对象。 |
| void HMS_HiAISingleOpOptions_Destroy (HiAI_SingleOpOptions **options); | 释放HiAI_SingleOpOptions对象。 |
| HiAI_SingleOpDescriptor* HMS_HiAISingleOpDescriptor_CreateConvolution(HiAISingleOpDescriptor_ConvolutionParam param); | 创建卷积类(普通卷积、转置卷积、深度卷积)的描述符对象。 |
| void HMS_HiAISingleOpDescriptor_Destroy (HiAI_SingleOpDescriptor **opDesc); | 释放HiAI_SingleOpDescriptor对象。 |
| HiAI_SingleOpExecutor* HMS_HiAISingleOpExecutor_CreateConvolution(HiAI_SingleOpExecutorConvolutionParam param); | 创建卷积类算子对应的HiAI_SingleOpExecutor对象。 |
| size_t HMS_HiAISingleOpExecutor_GetWorkspaceSize (const HiAI_SingleOpExecutor *executor); | 查询HiAI_SingleOpExecutor所需的ION内存工作空间的字节大小。 |
| OH_NN_ReturnCode HMS_HiAISingleOpExecutor_Init (HiAI_SingleOpExecutor *executor, void *workspace, size_t workspaceSize); | 加载HiAI_SingleOpExecutor。 |
| OH_NN_ReturnCode HMS_HiAISingleOpExecutor_Execute (HiAI_SingleOpExecutor *executor, HiAI_SingleOpTensor *input[], int32_t inputNum, HiAI_SingleOpTensor *output[], int32_t outputNum); | 执行同步运算推理。 |
| OH_NN_ReturnCode HMS_HiAISingleOpExecutor_Destroy (HiAI_SingleOpExecutor **executor); | 销毁HiAI_SingleOpExecutor对象,释放执行器占用的内存。 |
开发步骤
以下开发步骤以卷积单算子为例。
-
创建单算子执行器。
-
调用HMS_HiAISingleOpOptions_Create,创建单算子配置对象。
-
调用HMS_HiAISingleOpDescriptor_CreateConvolution,创建卷积类算子描述符对象。
-
调用HMS_HiAISingleOpTensor_CreateFromConst,分别创建卷积算子的权重、偏置单算子Tensor。
-
调用HMS_HiAISingleOpTensorDesc_Create,分别创建单算子输入Tensor、输出Tensor的描述对象。
-
调用HMS_HiAISingleOpExecutor_CreateConvolution,将上述创建好的卷积类算子描述符对象、卷积算子的权重Tensor、卷积算子的偏置Tensor、输入Tensor描述、输出Tensor描述作为输入,创建单算子执行器;
如果需要创建卷积算子与激活算子的融合算子执行器,还需要调用HMS_HiAISingleOpDescriptor_CreateActivation,创建激活类算子描述符对象,然后调用HMS_HiAISingleOpExecutor_CreateFusedConvolutionActivation创建融合算子执行器。
-
创建成功后,调用HMS_HiAISingleOpDescriptor_Destroy释放算子描述符对象,调用HMS_HiAISingleOpOptions_Destroy释放单算子创建配置对象。
-
-
创建输入/输出Tensor。
- 调用HMS_HiAISingleOpTensor_CreateFromTensorDesc,分别创建单算子输入Tensor、输出Tensor。
- 创建成功后,调用HMS_HiAISingleOpTensorDesc_Destroy释放Tensor描述符对象。
- 调用HMS_HiAISingleOpTensor_GetBuffer,获取输入/输出Tensor内部的Buffer对象。
- 调用HMS_HiAISingleOpBuffer_GetData,获取申请好的输入/输出ION内存地址,可用于该单算子在模型整网推理中的输入写入、输出读取。
-
加载单算子执行器。
- 调用HMS_HiAISingleOpExecutor_GetWorkspaceSize,获取已创建的单算子执行器在执行推理计算时需要的ION内存工作空间大小。
- 调用HMS_HiAISingleOpBuffer_Create,根据单算子执行器所需的ION内存工作空间大小创建足够的工作空间。
- 调用HMS_HiAISingleOpBuffer_GetData,获取申请好的ION内存工作空间的地址。
- 调用HMS_HiAISingleOpExecutor_Init,使用工作空间内存地址、工作空间大小,加载创建好的单算子执行器。
-
执行推理运算。
调用HMS_HiAISingleOpExecutor_Execute,执行同步运算推理。
-
卸载单算子执行器,释放资源。
- 调用HMS_HiAISingleOpTensor_Destroy,释放输入、输出Tensor对象
- 调用HMS_HiAISingleOpBuffer_Destroy,释放工作空间。
- 调用HMS_HiAISingleOpExecutor_Destroy,释放执行器对象。
示例说明
假定现在有一个深度卷积算子,输入维度为1x8x224x224,输入NCHW格式排布的float32类型数据,准备好NCHW排布的权重与偏置数据,调用单算子接口推理运算获得NCHW格式float32类型的输出可以参考如下示例代码:
#include "CANNKit/hiai_single_op.h"
#include <cstdio>
#include <cstdlib>
#include <cstring>
// 示例算子参数
// 单算子卷积模式
HiAI_SingleOpConvMode convMode = HIAI_SINGLEOP_CONV_MODE_DEPTHWISE;
int64_t strides[2] = {1, 1};
int64_t dilations[2] = {1, 1};
int64_t pads[4] = {0, 0, 0, 0};
int64_t groups = 1;
// 单算子填充模式
HiAI_SingleOpPadMode padMode = HIAI_SINGLEOP_PAD_MODE_SAME;
int64_t filterDims[4] = {8, 1, 3, 3};
size_t filterDataSize = 8 * 1 * 3 * 3 * sizeof(float);
void* filterData = malloc(filterDataSize);
int64_t biasDims[1] = {8};
size_t biasDataSize = 8 * sizeof(float);
void* biasData = malloc(biasDataSize);
int64_t inputDims[4] = {1, 8, 224, 224};
HiAI_SingleOpDataType inputDataType = HIAI_SINGLEOP_DT_FLOAT;
// 单算子张量排布格式
HiAI_SingleOpFormat inputFormat = HIAI_SINGLEOP_FORMAT_NCHW;
bool inputIsVirtual = false;
// 若不指定算子输出数据类型和排布格式,请设置数据类型为HIAI_SINGLEOP_DT_UNDEFINED,排布格式为HIAI_SINGLEOP_FORMAT_RESERVED
// 在单算子创建完成后,调用HMS_HiAISingleOpExecutor_UpdateOutputTensorDesc,将输出Tensor描述更新为硬件适配最优的数据类型和排布格式
int64_t outputDims[4] = {1, 8, 224, 224};
HiAI_SingleOpDataType outputDataType = HIAI_SINGLEOP_DT_FLOAT;
HiAI_SingleOpFormat outputFormat = HIAI_SINGLEOP_FORMAT_NCHW;
bool outputIsVirtual = false;
// 创建单算子执行器
HiAI_SingleOpOptions* options = HMS_HiAISingleOpOptions_Create();
HiAISingleOpDescriptor_ConvolutionParam convOpDescCreateParam = {convMode, {0}, {0}, {0}, groups, padMode};
memcpy(convOpDescCreateParam.strides, strides, 2 * sizeof(int64_t));
memcpy(convOpDescCreateParam.dilations, dilations, 2 * sizeof(int64_t));
memcpy(convOpDescCreateParam.pads, pads, 4 * sizeof(int64_t));
// 创建卷积类的描述符对象
HiAI_SingleOpDescriptor* convOpDesc = HMS_HiAISingleOpDescriptor_CreateConvolution(convOpDescCreateParam);
// 创建一个单算子tensor描述对象,根据维度、数据类型和格式
HiAI_SingleOpTensorDesc* filterDesc = HMS_HiAISingleOpTensorDesc_Create(filterDims, 4, HIAI_SINGLEOP_DT_FLOAT, HIAI_SINGLEOP_FORMAT_NCHW, false);
// 创建一个单算子tensor对象
HiAI_SingleOpTensor* filter = HMS_HiAISingleOpTensor_CreateFromConst(filterDesc, filterData, filterDataSize);
HiAI_SingleOpTensorDesc* biasDesc = HMS_HiAISingleOpTensorDesc_Create(biasDims, 1, HIAI_SINGLEOP_DT_FLOAT, HIAI_SINGLEOP_FORMAT_NCHW, false);
HiAI_SingleOpTensor* bias = HMS_HiAISingleOpTensor_CreateFromConst(biasDesc, biasData, biasDataSize);
HiAI_SingleOpTensorDesc* inputDesc = HMS_HiAISingleOpTensorDesc_Create(inputDims, 4, inputDataType, inputFormat, inputIsVirtual);
HiAI_SingleOpTensorDesc* outputDesc = HMS_HiAISingleOpTensorDesc_Create(outputDims, 4, outputDataType, outputFormat, outputIsVirtual);
// 构造单算子卷积executor参数
HiAI_SingleOpExecutorConvolutionParam executorCreateParam = {options, convOpDesc, inputDesc, outputDesc, filter, bias};
// 创建卷积单算子executor
HiAI_SingleOpExecutor* executor = HMS_HiAISingleOpExecutor_CreateConvolution(executorCreateParam);
if (executor == nullptr) {
printf("HMS_HiAISingleOp executor create failed. \n");
}
// 对不需要的资源建议即时销毁
HMS_HiAISingleOpTensorDesc_Destroy(&filterDesc);
HMS_HiAISingleOpTensorDesc_Destroy(&biasDesc);
HMS_HiAISingleOpOptions_Destroy(&options);
HMS_HiAISingleOpDescriptor_Destroy(&convOpDesc);
OH_NN_ReturnCode ret = HMS_HiAISingleOpTensor_Destroy(&filter);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp filter destroy failed.\n");
}
ret = HMS_HiAISingleOpTensor_Destroy(&bias);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp bias destroy failed.\n");
}
// 创建输入/输出Tensor
HiAI_SingleOpTensor* input = HMS_HiAISingleOpTensor_CreateFromTensorDesc(inputDesc);
HMS_HiAISingleOpTensorDesc_Destroy(&inputDesc);
HiAI_SingleOpTensor* output = HMS_HiAISingleOpTensor_CreateFromTensorDesc(outputDesc);
HMS_HiAISingleOpTensorDesc_Destroy(&outputDesc);
// 单算子输入Tensor和输出Tensor的内存必须为ION内存以节省拷贝开销
// 创建输入Tensor成功后,可以使用以下方式获取输入Tensor内的ION内存地址进行输入数据填装
// 输出Tensor内的ION内存地址也可以用以下方式获取,在推理计算成功后用于输出数据读取
HiAI_SingleOpBuffer* inputBuffer = HMS_HiAISingleOpTensor_GetBuffer(input);
void* inputData = HMS_HiAISingleOpBuffer_GetData(inputBuffer);
size_t inputDataSize = HMS_HiAISingleOpBuffer_GetSize(inputBuffer);
memset(inputData, 0, inputDataSize);
// 查询单算子执行器所需的ION内存工作空间的字节大小
size_t workspaceSize = HMS_HiAISingleOpExecutor_GetWorkspaceSize(executor);
// 若存在多个单算子执行器,各个执行器的工作空间内存可以复用,只需要申请所需的最大工作空间即可
HiAI_SingleOpBuffer* workspaceBuffer = HMS_HiAISingleOpBuffer_Create(workspaceSize);
void* workspace = HMS_HiAISingleOpBuffer_GetData(workspaceBuffer);
ret = HMS_HiAISingleOpExecutor_Init(executor, workspace, workspaceSize);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp executor init failed.\n");
}
// 执行推理运算
HiAI_SingleOpTensor* inputs[] = {input};
HiAI_SingleOpTensor* outputs[] = {output};
ret = HMS_HiAISingleOpExecutor_Execute(executor, inputs, 1, outputs, 1);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp executor execute failed.\n");
}
// 卸载单算子执行器,释放资源
ret = HMS_HiAISingleOpTensor_Destroy(&input);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp input destroy failed.\n");
}
ret = HMS_HiAISingleOpTensor_Destroy(&output);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp output destroy failed.\n");
}
ret = HMS_HiAISingleOpBuffer_Destroy(&workspaceBuffer);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp workspaceBuffer destroy failed.\n");
}
ret = HMS_HiAISingleOpExecutor_Destroy(&executor);
if (ret != OH_NN_SUCCESS) {
printf("HMS_HiAISingleOp executor destroy failed.\n");
}
free(filterData);
free(biasData);