文档管理中心

单算子应用

概述

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对象,释放执行器占用的内存。

开发步骤

以下开发步骤以卷积单算子为例。

  1. 创建单算子执行器。

    1. 调用HMS_HiAISingleOpOptions_Create,创建单算子配置对象。

    2. 调用HMS_HiAISingleOpDescriptor_CreateConvolution,创建卷积类算子描述符对象。

    3. 调用HMS_HiAISingleOpTensor_CreateFromConst,分别创建卷积算子的权重、偏置单算子Tensor。

    4. 调用HMS_HiAISingleOpTensorDesc_Create,分别创建单算子输入Tensor、输出Tensor的描述对象。

    5. 调用HMS_HiAISingleOpExecutor_CreateConvolution,将上述创建好的卷积类算子描述符对象、卷积算子的权重Tensor、卷积算子的偏置Tensor、输入Tensor描述、输出Tensor描述作为输入,创建单算子执行器;

      如果需要创建卷积算子与激活算子的融合算子执行器,还需要调用HMS_HiAISingleOpDescriptor_CreateActivation,创建激活类算子描述符对象,然后调用HMS_HiAISingleOpExecutor_CreateFusedConvolutionActivation创建融合算子执行器。

    6. 创建成功后,调用HMS_HiAISingleOpDescriptor_Destroy释放算子描述符对象,调用HMS_HiAISingleOpOptions_Destroy释放单算子创建配置对象。

  2. 创建输入/输出Tensor。

    1. 调用HMS_HiAISingleOpTensor_CreateFromTensorDesc,分别创建单算子输入Tensor、输出Tensor。
    2. 创建成功后,调用HMS_HiAISingleOpTensorDesc_Destroy释放Tensor描述符对象。
    3. 调用HMS_HiAISingleOpTensor_GetBuffer,获取输入/输出Tensor内部的Buffer对象。
    4. 调用HMS_HiAISingleOpBuffer_GetData,获取申请好的输入/输出ION内存地址,可用于该单算子在模型整网推理中的输入写入、输出读取。
  3. 加载单算子执行器。

    1. 调用HMS_HiAISingleOpExecutor_GetWorkspaceSize,获取已创建的单算子执行器在执行推理计算时需要的ION内存工作空间大小。
    2. 调用HMS_HiAISingleOpBuffer_Create,根据单算子执行器所需的ION内存工作空间大小创建足够的工作空间。
    3. 调用HMS_HiAISingleOpBuffer_GetData,获取申请好的ION内存工作空间的地址。
    4. 调用HMS_HiAISingleOpExecutor_Init,使用工作空间内存地址、工作空间大小,加载创建好的单算子执行器。
  4. 执行推理运算。

    调用HMS_HiAISingleOpExecutor_Execute,执行同步运算推理。

  5. 卸载单算子执行器,释放资源。

示例说明

假定现在有一个深度卷积算子,输入维度为1x8x224x224,输入NCHW格式排布的float32类型数据,准备好NCHW排布的权重与偏置数据,调用单算子接口推理运算获得NCHW格式float32类型的输出可以参考如下示例代码:

收起
自动换行
深色代码主题
复制
  1. // 示例算子参数
  2. // 单算子卷积模式
  3. HiAI_SingleOpConvMode convMode = HIAI_SINGLEOP_CONV_MODE_DEPTHWISE;
  4. int64_t strides[2] = {1, 1};
  5. int64_t dilations[2] = {1, 1};
  6. int64_t pads[4] = {0, 0, 0, 0};
  7. int64_t groups = 1;
  8. // 单算子填充模式
  9. HiAI_SingleOpPadMode padMode = HIAI_SINGLEOP_PAD_MODE_SAME;
  10. int64_t filterDims[4] = {8, 1, 3, 3};
  11. size_t filterDataSize = 8 * 1 * 3 * 3 * sizeof(float);
  12. void *filterData = malloc(filterDataSize);
  13. // ...
  14. int64_t biasDims[1] = {8};
  15. size_t biasDataSize = 8 * sizeof(float);
  16. void *biasData = malloc(biasDataSize);
  17. // ...
  18. int64_t inputDims[4] = {1, 8, 224, 224};
  19. HiAI_SingleOpDataType inputDataType = HIAI_SINGLEOP_DT_FLOAT;
  20. // 单算子张量排布格式
  21. HiAI_SingleOpFormat inputFormat = HIAI_SINGLEOP_FORMAT_NCHW;
  22. bool inputIsVirtual = false;
  23. // 若不指定算子输出数据类型和排布格式,请设置数据类型为HIAI_SINGLEOP_DT_UNDEFINED,排布格式为HIAI_SINGLEOP_FORMAT_RESERVED
  24. // 在单算子创建完成后,调用HMS_HiAISingleOpExecutor_UpdateOutputTensorDesc,将输出Tensor描述更新为硬件适配最优的数据类型和排布格式
  25. int64_t outputDims[4] = {1, 8, 224, 224};
  26. HiAI_SingleOpDataType outputDataType = HIAI_SINGLEOP_DT_FLOAT;
  27. HiAI_SingleOpFormat outputFormat = HIAI_SINGLEOP_FORMAT_NCHW;
  28. bool outputIsVirtual = false;
  29. // 创建单算子执行器
  30. options_ = HMS_HiAISingleOpOptions_Create();
  31. HiAISingleOpDescriptor_ConvolutionParam convOpDescCreateParam = {convMode, {0}, {0}, {0}, groups, padMode};
  32. memcpy(convOpDescCreateParam.strides, strides, opDescSize * sizeof(int64_t));
  33. memcpy(convOpDescCreateParam.dilations, dilations, opDescSize * sizeof(int64_t));
  34. memcpy(convOpDescCreateParam.pads, pads, tensorSize * sizeof(int64_t));
  35. // 创建卷积类的描述符对象
  36. convOpDesc_ = HMS_HiAISingleOpDescriptor_CreateConvolution(convOpDescCreateParam);
  37. // 创建一个单算子tensor描述对象,根据维度、数据类型和格式
  38. filterDesc_ = HMS_HiAISingleOpTensorDesc_Create(filterDims, tensorSize, inputDataType, inputFormat, false);
  39. // 创建一个单算子tensor对象
  40. filter_ = HMS_HiAISingleOpTensor_CreateFromConst(filterDesc_, filterData, filterDataSize);
  41. biasDesc_ = HMS_HiAISingleOpTensorDesc_Create(biasDims, 1, outputDataType, outputFormat, false);
  42. bias_ = HMS_HiAISingleOpTensor_CreateFromConst(biasDesc_, biasData, biasDataSize);
  43. inputDesc_ = HMS_HiAISingleOpTensorDesc_Create(inputDims, tensorSize, inputDataType, inputFormat, inputIsVirtual);
  44. outputDesc_ = HMS_HiAISingleOpTensorDesc_Create(outputDims, tensorSize, outputDataType, outputFormat,
  45. outputIsVirtual);
  46. // 构造单算子卷积 executor参数
  47. executorCreateParam_ = {options_, convOpDesc_, inputDesc_, outputDesc_, filter_, bias_};
  48. // ...
  49. // 创建卷积单算子executor
  50. executor_ = HMS_HiAISingleOpExecutor_CreateConvolution(executorCreateParam_);
  51. if (executor_ == nullptr) {
  52. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp executor create failed");
  53. // ...
  54. }
  55. // 对不需要的资源建议即时销毁
  56. HMS_HiAISingleOpTensorDesc_Destroy(&filterDesc_);
  57. HMS_HiAISingleOpTensorDesc_Destroy(&biasDesc_);
  58. HMS_HiAISingleOpOptions_Destroy(&options_);
  59. HMS_HiAISingleOpDescriptor_Destroy(&convOpDesc_);
  60. ret = HMS_HiAISingleOpTensor_Destroy(&filter_);
  61. if (ret != OH_NN_SUCCESS) {
  62. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp filter destroy failed");
  63. // ...
  64. }
  65. ret = HMS_HiAISingleOpTensor_Destroy(&bias_);
  66. if (ret != OH_NN_SUCCESS) {
  67. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp bias destroy failed");
  68. // ...
  69. }
  70. // ...
  71. // 统计算子构图耗时
  72. std::chrono::system_clock::time_point createTimeBegin = std::chrono::system_clock::now();
  73. // 创建输入/输出Tensor
  74. input_ = HMS_HiAISingleOpTensor_CreateFromTensorDesc(inputDesc_);
  75. output_ = HMS_HiAISingleOpTensor_CreateFromTensorDesc(outputDesc_);
  76. // 单算子输入Tensor和输出Tensor的内存必须为ION内存以节省拷贝开销
  77. // 创建输入Tensor成功后,可以使用以下方式获取输入Tensor内的ION内存地址进行输入数据填装
  78. // 输出Tensor内的ION内存地址也可以用以下方式获取,在推理计算成功后用于输出数据读取
  79. HiAI_SingleOpBuffer *inputBuffer = HMS_HiAISingleOpTensor_GetBuffer(input_);
  80. void *inputData = HMS_HiAISingleOpBuffer_GetData(inputBuffer);
  81. size_t inputDataSize = HMS_HiAISingleOpBuffer_GetSize(inputBuffer);
  82. memset(inputData, 0, inputDataSize);
  83. std::chrono::system_clock::time_point createTimeEnd = std::chrono::system_clock::now();
  84. createTensorTime = GetRunTime(createTimeBegin, createTimeEnd);
  85. HMS_HiAISingleOpTensorDesc_Destroy(&inputDesc_);
  86. HMS_HiAISingleOpTensorDesc_Destroy(&outputDesc_);
  87. // ...
  88. // 查询单算子执行器所需的ION内存工作空间的字节大小
  89. size_t workspaceSize = HMS_HiAISingleOpExecutor_GetWorkspaceSize(executor_);
  90. // 若存在多个单算子执行器,各个执行器的工作空间内存可以复用,只需要申请所需的最大工作空间即可
  91. workspaceBuffer_ = HMS_HiAISingleOpBuffer_Create(workspaceSize);
  92. void *workspace = HMS_HiAISingleOpBuffer_GetData(workspaceBuffer_);
  93. // 在调用该接口之前,需要申请执行器所需的工作空间内存
  94. OH_NN_ReturnCode ret = HMS_HiAISingleOpExecutor_Init(executor_, workspace, workspaceSize);
  95. if (ret != OH_NN_SUCCESS) {
  96. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp executor init failed");
  97. // ...
  98. }
  99. // ...
  100. // 多轮多次执行推理运算,统计算子的推理耗时
  101. for (size_t i = 0; i < times; i++) {
  102. for (size_t j = 0; j < opNum; j++) {
  103. // 执行推理运算
  104. HiAI_SingleOpTensor *inputs[] = {input_};
  105. HiAI_SingleOpTensor *outputs[] = {output_};
  106. std::chrono::system_clock::time_point executeTimeBegin = std::chrono::system_clock::now();
  107. OH_NN_ReturnCode ret = HMS_HiAISingleOpExecutor_Execute(executor_, inputs, 1, outputs, 1);
  108. if (ret != OH_NN_SUCCESS) {
  109. OH_LOG_ERROR(LOG_APP, "HMS_HiAISingleOp executor execute failed");
  110. // ...
  111. }
  112. std::chrono::system_clock::time_point executeTimeEnd = std::chrono::system_clock::now();
  113. auto executeElapsedTime = GetRunTime(executeTimeBegin, executeTimeEnd);
  114. OH_LOG_INFO(LOG_APP, "idx-%zu execute succ: %llu us", j, executeElapsedTime);
  115. aveTime[j] += executeElapsedTime;
  116. }
  117. OH_LOG_INFO(LOG_APP, "------ Round %zu ------ ", i);
  118. }
  119. // ...
  120. // 统计算子资源释放耗时
  121. std::chrono::system_clock::time_point destroyTimeBegin = std::chrono::system_clock::now();
  122. // 销毁输入Tensor,释放资源
  123. OH_NN_ReturnCode ret = HMS_HiAISingleOpTensor_Destroy(&input_);
  124. if (ret != OH_NN_SUCCESS) {
  125. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp input_ destroy failed");
  126. // ...
  127. }
  128. // 销毁输出Tensor,释放资源
  129. ret = HMS_HiAISingleOpTensor_Destroy(&output_);
  130. if (ret != OH_NN_SUCCESS) {
  131. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp output_ destroy failed");
  132. // ...
  133. }
  134. // 释放单算子Buffer对象
  135. ret = HMS_HiAISingleOpBuffer_Destroy(&workspaceBuffer_);
  136. if (ret != OH_NN_SUCCESS) {
  137. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp workspaceBuffer_ destroy failed");
  138. // ...
  139. }
  140. // 销毁单算子执行器,释放执行器占用的内存
  141. ret = HMS_HiAISingleOpExecutor_Destroy(&executor_);
  142. if (ret != OH_NN_SUCCESS) {
  143. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp executor destroy failed");
  144. // ...
  145. }
  146. std::chrono::system_clock::time_point destroyTimeEnd = std::chrono::system_clock::now();
  147. destroyTime = GetRunTime(destroyTimeBegin, destroyTimeEnd);
  148. // ...
  149. // 汇总各阶段耗时结果
  150. std::vector<float> outputs(returnArraySize, 0);
  151. if (!times_) {
  152. OH_LOG_ERROR(LOG_APP, "iteration times_ is not initialized or is zero");
  153. return outputs;
  154. }
  155. if (aveTime.empty()) {
  156. OH_LOG_INFO(LOG_APP, "HMS_HiAISingleOp_GetResult failed");
  157. return outputs;
  158. }
  159. // 获取构图时间,微妙转秒
  160. createTensorTime /= 1000000.0f;
  161. outputs[0] = createTensorTime;
  162. // 获取推理时间
  163. executeTime = 0;
  164. for (size_t i = 0; i < opNum; i++) {
  165. aveTime[i] /= times_;
  166. OH_LOG_INFO(LOG_APP, "idx-%zu average time: %.2f us", i, aveTime[i]);
  167. executeTime += aveTime[i];
  168. }
  169. OH_LOG_INFO(LOG_APP, "op average time sum: %.2f us", executeTime);
  170. executeTime /= 1000000.0f;
  171. outputs[1] = executeTime;
  172. // 获取资源释放时间
  173. destroyTime /= 1000000.0f;
  174. outputs[2] = destroyTime;
  175. OH_LOG_INFO(LOG_APP, "GetResult success");
  176. return outputs;
在 指南 中进行搜索
请输入您想要搜索的关键词