112e714ceSopenharmony_ci# Neural Network Runtime对接AI推理框架开发指导
212e714ceSopenharmony_ci
312e714ceSopenharmony_ci## 场景介绍
412e714ceSopenharmony_ci
512e714ceSopenharmony_ciNeural Network Runtime作为AI推理引擎和加速芯片的桥梁,为AI推理引擎提供精简的Native接口,满足推理引擎通过加速芯片执行端到端推理的需求。
612e714ceSopenharmony_ci
712e714ceSopenharmony_ci本文以图1展示的`Add`单算子模型为例,介绍Neural Network Runtime的开发流程。`Add`算子包含两个输入、一个参数和一个输出,其中的`activation`参数用于指定`Add`算子中激活函数的类型。
812e714ceSopenharmony_ci
912e714ceSopenharmony_ci**图1** Add单算子网络示意图
1012e714ceSopenharmony_ci!["Add单算子网络示意图"](figures/neural_network_runtime_add_op_model.png)
1112e714ceSopenharmony_ci
1212e714ceSopenharmony_ci## 环境准备
1312e714ceSopenharmony_ci
1412e714ceSopenharmony_ci### 环境要求
1512e714ceSopenharmony_ci
1612e714ceSopenharmony_ciNeural Network Runtime部件的环境要求如下:
1712e714ceSopenharmony_ci
1812e714ceSopenharmony_ci- 开发环境:Ubuntu 18.04及以上。
1912e714ceSopenharmony_ci- 接入设备:系统定义的标准设备,系统中内置AI硬件驱动并已接入Neural Network Runtime。
2012e714ceSopenharmony_ci
2112e714ceSopenharmony_ci由于Neural Network Runtime通过OpenHarmony Native API对外开放,需要通过OpenHarmony的Native开发套件编译Neural Network Runtime应用。在社区的每日构建中下载对应系统版本的ohos-sdk压缩包,从压缩包中提取对应平台的Native开发套件。以Linux为例,Native开发套件的压缩包命名为`native-linux-{版本号}.zip`。
2212e714ceSopenharmony_ci
2312e714ceSopenharmony_ci### 环境搭建
2412e714ceSopenharmony_ci
2512e714ceSopenharmony_ci1. 打开Ubuntu编译服务器的终端。
2612e714ceSopenharmony_ci2. 把下载好的Native开发套件压缩包拷贝至当前用户根目录下。
2712e714ceSopenharmony_ci3. 执行以下命令解压Native开发套件的压缩包。
2812e714ceSopenharmony_ci    ```shell
2912e714ceSopenharmony_ci    unzip native-linux-{版本号}.zip
3012e714ceSopenharmony_ci    ```
3112e714ceSopenharmony_ci
3212e714ceSopenharmony_ci    解压缩后的内容如下(随版本迭代,目录下的内容可能发生变化,请以最新版本的Native API为准):
3312e714ceSopenharmony_ci    ```text
3412e714ceSopenharmony_ci    native/
3512e714ceSopenharmony_ci    ├── build // 交叉编译工具链
3612e714ceSopenharmony_ci    ├── build-tools // 编译构建工具
3712e714ceSopenharmony_ci    ├── docs
3812e714ceSopenharmony_ci    ├── llvm
3912e714ceSopenharmony_ci    ├── nativeapi_syscap_config.json
4012e714ceSopenharmony_ci    ├── ndk_system_capability.json
4112e714ceSopenharmony_ci    ├── NOTICE.txt
4212e714ceSopenharmony_ci    ├── oh-uni-package.json
4312e714ceSopenharmony_ci    └── sysroot // Native API头文件和库
4412e714ceSopenharmony_ci    ```
4512e714ceSopenharmony_ci## 接口说明
4612e714ceSopenharmony_ci
4712e714ceSopenharmony_ci这里给出Neural Network Runtime开发流程中通用的接口,具体请见下列表格。
4812e714ceSopenharmony_ci
4912e714ceSopenharmony_ci### 结构体
5012e714ceSopenharmony_ci
5112e714ceSopenharmony_ci| 结构体名称 | 描述 |
5212e714ceSopenharmony_ci| --------- | ---- |
5312e714ceSopenharmony_ci| typedef struct OH_NNModel OH_NNModel | Neural Network Runtime的模型句柄,用于构造模型。 |
5412e714ceSopenharmony_ci| typedef struct OH_NNCompilation OH_NNCompilation | Neural Network Runtime的编译器句柄,用于编译AI模型。 |
5512e714ceSopenharmony_ci| typedef struct OH_NNExecutor OH_NNExecutor | Neural Network Runtime的执行器句柄,用于在指定设备上执行推理计算。 |
5612e714ceSopenharmony_ci| typedef struct NN_QuantParam NN_QuantParam | Neural Network Runtime的量化参数句柄,用于在构造模型时指定张量的量化参数。 |
5712e714ceSopenharmony_ci| typedef struct NN_TensorDesc NN_TensorDesc | Neural Network Runtime的张量描述句柄,用于描述张量的各类属性,例如数据布局、数据类型、形状等。 |
5812e714ceSopenharmony_ci| typedef struct NN_Tensor NN_Tensor | Neural Network Runtime的张量句柄,用于设置执行器的推理输入和输出张量。 |
5912e714ceSopenharmony_ci
6012e714ceSopenharmony_ci### 模型构造接口
6112e714ceSopenharmony_ci
6212e714ceSopenharmony_ci| 接口名称 | 描述 |
6312e714ceSopenharmony_ci| ------- | --- |
6412e714ceSopenharmony_ci| OH_NNModel_Construct() | 创建OH_NNModel类型的模型实例。 |
6512e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNModel_AddTensorToModel(OH_NNModel *model, const NN_TensorDesc *tensorDesc) | 向模型实例中添加张量。 |
6612e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNModel_SetTensorData(OH_NNModel *model, uint32_t index, const void *dataBuffer, size_t length) | 设置张量的数值。 |
6712e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNModel_AddOperation(OH_NNModel *model, OH_NN_OperationType op, const OH_NN_UInt32Array *paramIndices, const OH_NN_UInt32Array *inputIndices, const OH_NN_UInt32Array *outputIndices) | 向模型实例中添加算子。 |
6812e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNModel_SpecifyInputsAndOutputs(OH_NNModel *model, const OH_NN_UInt32Array *inputIndices, const OH_NN_UInt32Array *outputIndices) | 指定模型的输入和输出张量的索引值。 |
6912e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNModel_Finish(OH_NNModel *model) | 完成模型构图。|
7012e714ceSopenharmony_ci| void OH_NNModel_Destroy(OH_NNModel **model) | 销毁模型实例。 |
7112e714ceSopenharmony_ci
7212e714ceSopenharmony_ci
7312e714ceSopenharmony_ci### 模型编译接口
7412e714ceSopenharmony_ci
7512e714ceSopenharmony_ci| 接口名称 | 描述 |
7612e714ceSopenharmony_ci| ------- | --- |
7712e714ceSopenharmony_ci| OH_NNCompilation *OH_NNCompilation_Construct(const OH_NNModel *model) | 基于模型实例创建OH_NNCompilation类型的编译实例。 |
7812e714ceSopenharmony_ci| OH_NNCompilation *OH_NNCompilation_ConstructWithOfflineModelFile(const char *modelPath) | 基于离线模型文件路径创建OH_NNCompilation类型的编译实例。 |
7912e714ceSopenharmony_ci| OH_NNCompilation *OH_NNCompilation_ConstructWithOfflineModelBuffer(const void *modelBuffer, size_t modelSize) | 基于离线模型文件内存创建OH_NNCompilation类型的编译实例。 |
8012e714ceSopenharmony_ci| OH_NNCompilation *OH_NNCompilation_ConstructForCache() | 创建一个空的编译实例,以便稍后从模型缓存中恢复。 |
8112e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_ExportCacheToBuffer(OH_NNCompilation *compilation, const void *buffer, size_t length, size_t *modelSize) | 将模型缓存写入到指定内存区域。 |
8212e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_ImportCacheFromBuffer(OH_NNCompilation *compilation, const void *buffer, size_t modelSize) | 从指定内存区域读取模型缓存。 |
8312e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_AddExtensionConfig(OH_NNCompilation *compilation, const char *configName, const void *configValue, const size_t configValueSize) | 为自定义硬件属性添加扩展配置,具体硬件的扩展属性名称和属性值需要从硬件厂商的文档中获取。 |
8412e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_SetDevice(OH_NNCompilation *compilation, size_t deviceID) | 指定模型编译和计算的硬件,可通过设备管理接口获取。 |
8512e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_SetCache(OH_NNCompilation *compilation, const char *cachePath, uint32_t version) | 设置编译模型的缓存目录和版本。 |
8612e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_SetPerformanceMode(OH_NNCompilation *compilation, OH_NN_PerformanceMode performanceMode) | 设置模型计算的性能模式。 |
8712e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_SetPriority(OH_NNCompilation *compilation, OH_NN_Priority priority) | 设置模型计算的优先级。 |
8812e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_EnableFloat16(OH_NNCompilation *compilation, bool enableFloat16) | 是否以float16的浮点数精度计算。 |
8912e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNCompilation_Build(OH_NNCompilation *compilation) | 执行模型编译。 |
9012e714ceSopenharmony_ci| void OH_NNCompilation_Destroy(OH_NNCompilation **compilation) | 销毁编译实例。 |
9112e714ceSopenharmony_ci
9212e714ceSopenharmony_ci### 张量描述接口
9312e714ceSopenharmony_ci
9412e714ceSopenharmony_ci| 接口名称 | 描述 |
9512e714ceSopenharmony_ci| ------- | --- |
9612e714ceSopenharmony_ci| NN_TensorDesc *OH_NNTensorDesc_Create() | 创建一个张量描述实例,用于后续创建张量。 |
9712e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_SetName(NN_TensorDesc *tensorDesc, const char *name) | 设置张量描述的名称。 |
9812e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_GetName(const NN_TensorDesc *tensorDesc, const char **name) | 获取张量描述的名称。 |
9912e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_SetDataType(NN_TensorDesc *tensorDesc, OH_NN_DataType dataType) | 设置张量描述的数据类型。 |
10012e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_GetDataType(const NN_TensorDesc *tensorDesc, OH_NN_DataType *dataType) | 获取张量描述的数据类型。 |
10112e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_SetShape(NN_TensorDesc *tensorDesc, const int32_t *shape, size_t shapeLength) | 设置张量描述的形状。 |
10212e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_GetShape(const NN_TensorDesc *tensorDesc, int32_t **shape, size_t *shapeLength) | 获取张量描述的形状。 |
10312e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_SetFormat(NN_TensorDesc *tensorDesc, OH_NN_Format format) | 设置张量描述的数据布局。 |
10412e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_GetFormat(const NN_TensorDesc *tensorDesc, OH_NN_Format *format) | 获取张量描述的数据布局。 |
10512e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_GetElementCount(const NN_TensorDesc *tensorDesc, size_t *elementCount) | 获取张量描述的元素个数。 |
10612e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_GetByteSize(const NN_TensorDesc *tensorDesc, size_t *byteSize) | 获取基于张量描述的形状和数据类型计算的数据占用字节数。 |
10712e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensorDesc_Destroy(NN_TensorDesc **tensorDesc) | 销毁张量描述实例。 |
10812e714ceSopenharmony_ci
10912e714ceSopenharmony_ci### 张量接口
11012e714ceSopenharmony_ci
11112e714ceSopenharmony_ci| 接口名称 | 描述 |
11212e714ceSopenharmony_ci| ------- | --- |
11312e714ceSopenharmony_ci| NN_Tensor* OH_NNTensor_Create(size_t deviceID, NN_TensorDesc *tensorDesc) | 从张量描述创建张量实例,会申请设备共享内存。 |
11412e714ceSopenharmony_ci| NN_Tensor* OH_NNTensor_CreateWithSize(size_t deviceID, NN_TensorDesc *tensorDesc, size_t size) | 按照指定内存大小和张量描述创建张量实例,会申请设备共享内存。 |
11512e714ceSopenharmony_ci| NN_Tensor* OH_NNTensor_CreateWithFd(size_t deviceID, NN_TensorDesc *tensorDesc, int fd, size_t size, size_t offset) | 按照指定共享内存的文件描述符和张量描述创建张量实例,从而可以复用其他张量的设备共享内存。 |
11612e714ceSopenharmony_ci| NN_TensorDesc* OH_NNTensor_GetTensorDesc(const NN_Tensor *tensor) | 获取张量内部的张量描述实例指针,从而可读取张量的属性,例如数据类型、形状等。 |
11712e714ceSopenharmony_ci| void* OH_NNTensor_GetDataBuffer(const NN_Tensor *tensor) | 获取张量数据的内存地址,可以读写张量数据。 |
11812e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensor_GetFd(const NN_Tensor *tensor, int *fd) | 获取张量数据所在共享内存的文件描述符,文件描述符fd对应了一块设备共享内存。 |
11912e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensor_GetSize(const NN_Tensor *tensor, size_t *size) | 获取张量数据所在共享内存的大小。 |
12012e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensor_GetOffset(const NN_Tensor *tensor, size_t *offset) | 获取张量数据所在共享内存上的偏移量,张量数据可使用的大小为所在共享内存的大小减去偏移量。 |
12112e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNTensor_Destroy(NN_Tensor **tensor) | 销毁张量实例。 |
12212e714ceSopenharmony_ci
12312e714ceSopenharmony_ci### 执行推理接口
12412e714ceSopenharmony_ci
12512e714ceSopenharmony_ci| 接口名称 | 描述 |
12612e714ceSopenharmony_ci| ------- | --- |
12712e714ceSopenharmony_ci| OH_NNExecutor *OH_NNExecutor_Construct(OH_NNCompilation *compilation) | 创建OH_NNExecutor类型的执行器实例。 |
12812e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_GetOutputShape(OH_NNExecutor *executor, uint32_t outputIndex, int32_t **shape, uint32_t *shapeLength) | 获取输出张量的维度信息,用于输出张量具有动态形状的情况。 |
12912e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_GetInputCount(const OH_NNExecutor *executor, size_t *inputCount) | 获取输入张量的数量。 |
13012e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_GetOutputCount(const OH_NNExecutor *executor, size_t *outputCount) | 获取输出张量的数量。 |
13112e714ceSopenharmony_ci| NN_TensorDesc* OH_NNExecutor_CreateInputTensorDesc(const OH_NNExecutor *executor, size_t index) | 由指定索引值创建一个输入张量的描述,用于读取张量的属性或创建张量实例。 |
13212e714ceSopenharmony_ci| NN_TensorDesc* OH_NNExecutor_CreateOutputTensorDesc(const OH_NNExecutor *executor, size_t index) | 由指定索引值创建一个输出张量的描述,用于读取张量的属性或创建张量实例。 |
13312e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_GetInputDimRange(const OH_NNExecutor *executor, size_t index, size_t **minInputDims, size_t **maxInputDims, size_t *shapeLength) |获取所有输入张量的维度范围。当输入张量具有动态形状时,不同设备可能支持不同的维度范围。 |
13412e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_SetOnRunDone(OH_NNExecutor *executor, NN_OnRunDone onRunDone) | 设置异步推理结束后的回调处理函数,回调函数定义详见接口文档。 |
13512e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_SetOnServiceDied(OH_NNExecutor *executor, NN_OnServiceDied onServiceDied) | 设置异步推理执行期间设备驱动服务突然死亡时的回调处理函数,回调函数定义详见接口文档。 |
13612e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_RunSync(OH_NNExecutor *executor, NN_Tensor *inputTensor[], size_t inputCount, NN_Tensor *outputTensor[], size_t outputCount) | 执行同步推理。 |
13712e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNExecutor_RunAsync(OH_NNExecutor *executor, NN_Tensor *inputTensor[], size_t inputCount, NN_Tensor *outputTensor[], size_t outputCount, int32_t timeout, void *userData) | 执行异步推理。 |
13812e714ceSopenharmony_ci| void OH_NNExecutor_Destroy(OH_NNExecutor **executor) | 销毁执行器实例。 |
13912e714ceSopenharmony_ci
14012e714ceSopenharmony_ci### 设备管理接口
14112e714ceSopenharmony_ci
14212e714ceSopenharmony_ci| 接口名称 | 描述 |
14312e714ceSopenharmony_ci| ------- | --- |
14412e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNDevice_GetAllDevicesID(const size_t **allDevicesID, uint32_t *deviceCount) | 获取对接到Neural Network Runtime的所有硬件ID。 |
14512e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNDevice_GetName(size_t deviceID, const char **name) | 获取指定硬件的名称。 |
14612e714ceSopenharmony_ci| OH_NN_ReturnCode OH_NNDevice_GetType(size_t deviceID, OH_NN_DeviceType *deviceType) | 获取指定硬件的类别信息。 |
14712e714ceSopenharmony_ci
14812e714ceSopenharmony_ci
14912e714ceSopenharmony_ci## 开发步骤
15012e714ceSopenharmony_ci
15112e714ceSopenharmony_ciNeural Network Runtime的开发流程主要包含**模型构造**、**模型编译**和**推理执行**三个阶段。以下开发步骤以`Add`单算子模型为例,介绍调用Neural Network Runtime接口,开发应用的过程。
15212e714ceSopenharmony_ci
15312e714ceSopenharmony_ci1. 创建应用样例文件。
15412e714ceSopenharmony_ci
15512e714ceSopenharmony_ci    首先,创建Neural Network Runtime应用样例的源文件。在项目目录下执行以下命令,创建`nnrt_example/`目录,并在目录下创建 `nnrt_example.cpp` 源文件。
15612e714ceSopenharmony_ci
15712e714ceSopenharmony_ci    ```shell
15812e714ceSopenharmony_ci    mkdir ~/nnrt_example && cd ~/nnrt_example
15912e714ceSopenharmony_ci    touch nnrt_example.cpp
16012e714ceSopenharmony_ci    ```
16112e714ceSopenharmony_ci
16212e714ceSopenharmony_ci2. 导入Neural Network Runtime。
16312e714ceSopenharmony_ci
16412e714ceSopenharmony_ci    在 `nnrt_example.cpp` 文件的开头添加以下代码,引入Neural Network Runtime。
16512e714ceSopenharmony_ci
16612e714ceSopenharmony_ci    ```cpp
16712e714ceSopenharmony_ci    #include <iostream>
16812e714ceSopenharmony_ci    #include <cstdarg>
16912e714ceSopenharmony_ci    #include "hilog/log.h"
17012e714ceSopenharmony_ci    #include "neural_network_runtime/neural_network_runtime.h"
17112e714ceSopenharmony_ci    ```
17212e714ceSopenharmony_ci
17312e714ceSopenharmony_ci3. 定义日志打印、设置输入数据、数据打印等辅助函数。
17412e714ceSopenharmony_ci
17512e714ceSopenharmony_ci    ```cpp
17612e714ceSopenharmony_ci    #define LOG_DOMAIN 0xD002101
17712e714ceSopenharmony_ci    #define LOG_TAG "NNRt"
17812e714ceSopenharmony_ci    #define LOGD(...) OH_LOG_DEBUG(LOG_APP, __VA_ARGS__)
17912e714ceSopenharmony_ci    #define LOGI(...) OH_LOG_INFO(LOG_APP, __VA_ARGS__)
18012e714ceSopenharmony_ci    #define LOGW(...) OH_LOG_WARN(LOG_APP, __VA_ARGS__)
18112e714ceSopenharmony_ci    #define LOGE(...) OH_LOG_ERROR(LOG_APP, __VA_ARGS__)
18212e714ceSopenharmony_ci    #define LOGF(...) OH_LOG_FATAL(LOG_APP, __VA_ARGS__)
18312e714ceSopenharmony_ci
18412e714ceSopenharmony_ci    // 返回值检查宏
18512e714ceSopenharmony_ci    #define CHECKNEQ(realRet, expectRet, retValue, ...) \
18612e714ceSopenharmony_ci        do { \
18712e714ceSopenharmony_ci            if ((realRet) != (expectRet)) { \
18812e714ceSopenharmony_ci                printf(__VA_ARGS__); \
18912e714ceSopenharmony_ci                return (retValue); \
19012e714ceSopenharmony_ci            } \
19112e714ceSopenharmony_ci        } while (0)
19212e714ceSopenharmony_ci
19312e714ceSopenharmony_ci    #define CHECKEQ(realRet, expectRet, retValue, ...) \
19412e714ceSopenharmony_ci        do { \
19512e714ceSopenharmony_ci            if ((realRet) == (expectRet)) { \
19612e714ceSopenharmony_ci                printf(__VA_ARGS__); \
19712e714ceSopenharmony_ci                return (retValue); \
19812e714ceSopenharmony_ci            } \
19912e714ceSopenharmony_ci        } while (0)
20012e714ceSopenharmony_ci
20112e714ceSopenharmony_ci    // 设置输入数据用于推理
20212e714ceSopenharmony_ci    OH_NN_ReturnCode SetInputData(NN_Tensor* inputTensor[], size_t inputSize)
20312e714ceSopenharmony_ci    {
20412e714ceSopenharmony_ci        OH_NN_DataType dataType(OH_NN_FLOAT32);
20512e714ceSopenharmony_ci        OH_NN_ReturnCode ret{OH_NN_FAILED};
20612e714ceSopenharmony_ci        size_t elementCount = 0;
20712e714ceSopenharmony_ci        for (size_t i = 0; i < inputSize; ++i) {
20812e714ceSopenharmony_ci            // 获取张量的数据内存
20912e714ceSopenharmony_ci            auto data = OH_NNTensor_GetDataBuffer(inputTensor[i]);
21012e714ceSopenharmony_ci            CHECKEQ(data, nullptr, OH_NN_FAILED, "Failed to get data buffer.");
21112e714ceSopenharmony_ci            // 获取张量的描述
21212e714ceSopenharmony_ci            auto desc = OH_NNTensor_GetTensorDesc(inputTensor[i]);
21312e714ceSopenharmony_ci            CHECKEQ(desc, nullptr, OH_NN_FAILED, "Failed to get desc.");
21412e714ceSopenharmony_ci            // 获取张量的数据类型
21512e714ceSopenharmony_ci            ret = OH_NNTensorDesc_GetDataType(desc, &dataType);
21612e714ceSopenharmony_ci            CHECKNEQ(ret, OH_NN_SUCCESS, OH_NN_FAILED, "Failed to get data type.");
21712e714ceSopenharmony_ci            // 获取张量的元素个数
21812e714ceSopenharmony_ci            ret = OH_NNTensorDesc_GetElementCount(desc, &elementCount);
21912e714ceSopenharmony_ci            CHECKNEQ(ret, OH_NN_SUCCESS, OH_NN_FAILED, "Failed to get element count.");
22012e714ceSopenharmony_ci            switch(dataType) {
22112e714ceSopenharmony_ci                case OH_NN_FLOAT32: {
22212e714ceSopenharmony_ci                    float* floatValue = reinterpret_cast<float*>(data);
22312e714ceSopenharmony_ci                    for (size_t j = 0; j < elementCount; ++j) {
22412e714ceSopenharmony_ci                        floatValue[j] = static_cast<float>(j);
22512e714ceSopenharmony_ci                    }
22612e714ceSopenharmony_ci                    break;
22712e714ceSopenharmony_ci                }
22812e714ceSopenharmony_ci                case OH_NN_INT32: {
22912e714ceSopenharmony_ci                    int* intValue = reinterpret_cast<int*>(data);
23012e714ceSopenharmony_ci                    for (size_t j = 0; j < elementCount; ++j) {
23112e714ceSopenharmony_ci                        intValue[j] = static_cast<int>(j);
23212e714ceSopenharmony_ci                    }
23312e714ceSopenharmony_ci                    break;
23412e714ceSopenharmony_ci                }
23512e714ceSopenharmony_ci                default:
23612e714ceSopenharmony_ci                    return OH_NN_FAILED;
23712e714ceSopenharmony_ci            }
23812e714ceSopenharmony_ci        }
23912e714ceSopenharmony_ci        return OH_NN_SUCCESS;
24012e714ceSopenharmony_ci    }
24112e714ceSopenharmony_ci
24212e714ceSopenharmony_ci    OH_NN_ReturnCode Print(NN_Tensor* outputTensor[], size_t outputSize)
24312e714ceSopenharmony_ci    {
24412e714ceSopenharmony_ci        OH_NN_DataType dataType(OH_NN_FLOAT32);
24512e714ceSopenharmony_ci        OH_NN_ReturnCode ret{OH_NN_FAILED};
24612e714ceSopenharmony_ci        size_t elementCount = 0;
24712e714ceSopenharmony_ci        for (size_t i = 0; i < outputSize; ++i) {
24812e714ceSopenharmony_ci            auto data = OH_NNTensor_GetDataBuffer(outputTensor[i]);
24912e714ceSopenharmony_ci            CHECKEQ(data, nullptr, OH_NN_FAILED, "Failed to get data buffer.");
25012e714ceSopenharmony_ci            auto desc = OH_NNTensor_GetTensorDesc(outputTensor[i]);
25112e714ceSopenharmony_ci            CHECKEQ(desc, nullptr, OH_NN_FAILED, "Failed to get desc.");
25212e714ceSopenharmony_ci            ret = OH_NNTensorDesc_GetDataType(desc, &dataType);
25312e714ceSopenharmony_ci            CHECKNEQ(ret, OH_NN_SUCCESS, OH_NN_FAILED, "Failed to get data type.");
25412e714ceSopenharmony_ci            ret = OH_NNTensorDesc_GetElementCount(desc, &elementCount);
25512e714ceSopenharmony_ci            CHECKNEQ(ret, OH_NN_SUCCESS, OH_NN_FAILED, "Failed to get element count.");
25612e714ceSopenharmony_ci            switch(dataType) {
25712e714ceSopenharmony_ci                case OH_NN_FLOAT32: {
25812e714ceSopenharmony_ci                    float* floatValue = reinterpret_cast<float*>(data);
25912e714ceSopenharmony_ci                    for (size_t j = 0; j < elementCount; ++j) {
26012e714ceSopenharmony_ci                        std::cout << "Output index: " << j << ", value is: " << floatValue[j] << "." << std::endl;
26112e714ceSopenharmony_ci                    }
26212e714ceSopenharmony_ci                    break;
26312e714ceSopenharmony_ci                }
26412e714ceSopenharmony_ci                case OH_NN_INT32: {
26512e714ceSopenharmony_ci                    int* intValue = reinterpret_cast<int*>(data);
26612e714ceSopenharmony_ci                    for (size_t j = 0; j < elementCount; ++j) {
26712e714ceSopenharmony_ci                        std::cout << "Output index: " << j << ", value is: " << intValue[j] << "." << std::endl;
26812e714ceSopenharmony_ci                    }
26912e714ceSopenharmony_ci                    break;
27012e714ceSopenharmony_ci                }
27112e714ceSopenharmony_ci                default:
27212e714ceSopenharmony_ci                    return OH_NN_FAILED;
27312e714ceSopenharmony_ci            }
27412e714ceSopenharmony_ci        }
27512e714ceSopenharmony_ci
27612e714ceSopenharmony_ci        return OH_NN_SUCCESS;
27712e714ceSopenharmony_ci    }
27812e714ceSopenharmony_ci    ```
27912e714ceSopenharmony_ci
28012e714ceSopenharmony_ci4. 构造模型。
28112e714ceSopenharmony_ci
28212e714ceSopenharmony_ci    使用Neural Network Runtime的模型构造接口,构造`Add`单算子样例模型。
28312e714ceSopenharmony_ci
28412e714ceSopenharmony_ci    ```cpp
28512e714ceSopenharmony_ci    OH_NN_ReturnCode BuildModel(OH_NNModel** pmodel)
28612e714ceSopenharmony_ci    {
28712e714ceSopenharmony_ci        // 创建模型实例model,进行模型构造
28812e714ceSopenharmony_ci        OH_NNModel* model = OH_NNModel_Construct();
28912e714ceSopenharmony_ci        CHECKEQ(model, nullptr, -1, "Create model failed.");
29012e714ceSopenharmony_ci
29112e714ceSopenharmony_ci        // 添加Add算子的第一个输入张量,类型为float32,张量形状为[1, 2, 2, 3]
29212e714ceSopenharmony_ci        NN_TensorDesc* tensorDesc = OH_NNTensorDesc_Create();
29312e714ceSopenharmony_ci        CHECKEQ(tensorDesc, nullptr, -1, "Create TensorDesc failed.");
29412e714ceSopenharmony_ci
29512e714ceSopenharmony_ci        int32_t inputDims[4] = {1, 2, 2, 3};
29612e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetShape(tensorDesc, inputDims, 4);
29712e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc shape failed.");
29812e714ceSopenharmony_ci
29912e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetDataType(tensorDesc, OH_NN_FLOAT32);
30012e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc data type failed.");
30112e714ceSopenharmony_ci
30212e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetFormat(tensorDesc, OH_NN_FORMAT_NONE);
30312e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc format failed.");
30412e714ceSopenharmony_ci
30512e714ceSopenharmony_ci        returnCode = OH_NNModel_AddTensorToModel(model, tensorDesc);
30612e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Add first TensorDesc to model failed.");
30712e714ceSopenharmony_ci
30812e714ceSopenharmony_ci        returnCode = OH_NNModel_SetTensorType(model, 0, OH_NN_TENSOR);
30912e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set model tensor type failed.");
31012e714ceSopenharmony_ci
31112e714ceSopenharmony_ci        // 添加Add算子的第二个输入张量,类型为float32,张量形状为[1, 2, 2, 3]
31212e714ceSopenharmony_ci        tensorDesc = OH_NNTensorDesc_Create();
31312e714ceSopenharmony_ci        CHECKEQ(tensorDesc, nullptr, -1, "Create TensorDesc failed.");
31412e714ceSopenharmony_ci
31512e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetShape(tensorDesc, inputDims, 4);
31612e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc shape failed.");
31712e714ceSopenharmony_ci
31812e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetDataType(tensorDesc, OH_NN_FLOAT32);
31912e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc data type failed.");
32012e714ceSopenharmony_ci
32112e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetFormat(tensorDesc, OH_NN_FORMAT_NONE);
32212e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc format failed.");
32312e714ceSopenharmony_ci
32412e714ceSopenharmony_ci        returnCode = OH_NNModel_AddTensorToModel(model, tensorDesc);
32512e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Add second TensorDesc to model failed.");
32612e714ceSopenharmony_ci
32712e714ceSopenharmony_ci        returnCode = OH_NNModel_SetTensorType(model, 1, OH_NN_TENSOR);
32812e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set model tensor type failed.");
32912e714ceSopenharmony_ci
33012e714ceSopenharmony_ci        // 添加Add算子的参数张量,该参数张量用于指定激活函数的类型,张量的数据类型为int8。
33112e714ceSopenharmony_ci        tensorDesc = OH_NNTensorDesc_Create();
33212e714ceSopenharmony_ci        CHECKEQ(tensorDesc, nullptr, -1, "Create TensorDesc failed.");
33312e714ceSopenharmony_ci
33412e714ceSopenharmony_ci        int32_t activationDims = 1;
33512e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetShape(tensorDesc, &activationDims, 1);
33612e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc shape failed.");
33712e714ceSopenharmony_ci
33812e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetDataType(tensorDesc, OH_NN_INT8);
33912e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc data type failed.");
34012e714ceSopenharmony_ci
34112e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetFormat(tensorDesc, OH_NN_FORMAT_NONE);
34212e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc format failed.");
34312e714ceSopenharmony_ci
34412e714ceSopenharmony_ci        returnCode = OH_NNModel_AddTensorToModel(model, tensorDesc);
34512e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Add second TensorDesc to model failed.");
34612e714ceSopenharmony_ci
34712e714ceSopenharmony_ci        returnCode = OH_NNModel_SetTensorType(model, 2, OH_NN_ADD_ACTIVATIONTYPE);
34812e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set model tensor type failed.");
34912e714ceSopenharmony_ci
35012e714ceSopenharmony_ci        // 将激活函数类型设置为OH_NNBACKEND_FUSED_NONE,表示该算子不添加激活函数。
35112e714ceSopenharmony_ci        int8_t activationValue = OH_NN_FUSED_NONE;
35212e714ceSopenharmony_ci        returnCode = OH_NNModel_SetTensorData(model, 2, &activationValue, sizeof(int8_t));
35312e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set model tensor data failed.");
35412e714ceSopenharmony_ci
35512e714ceSopenharmony_ci        // 设置Add算子的输出张量,类型为float32,张量形状为[1, 2, 2, 3]
35612e714ceSopenharmony_ci        tensorDesc = OH_NNTensorDesc_Create();
35712e714ceSopenharmony_ci        CHECKEQ(tensorDesc, nullptr, -1, "Create TensorDesc failed.");
35812e714ceSopenharmony_ci
35912e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetShape(tensorDesc, inputDims, 4);
36012e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc shape failed.");
36112e714ceSopenharmony_ci
36212e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetDataType(tensorDesc, OH_NN_FLOAT32);
36312e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc data type failed.");
36412e714ceSopenharmony_ci
36512e714ceSopenharmony_ci        returnCode = OH_NNTensorDesc_SetFormat(tensorDesc, OH_NN_FORMAT_NONE);
36612e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set TensorDesc format failed.");
36712e714ceSopenharmony_ci
36812e714ceSopenharmony_ci        returnCode = OH_NNModel_AddTensorToModel(model, tensorDesc);
36912e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Add forth TensorDesc to model failed.");
37012e714ceSopenharmony_ci
37112e714ceSopenharmony_ci        returnCode = OH_NNModel_SetTensorType(model, 3, OH_NN_TENSOR);
37212e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Set model tensor type failed.");
37312e714ceSopenharmony_ci
37412e714ceSopenharmony_ci        // 指定Add算子的输入张量、参数张量和输出张量的索引
37512e714ceSopenharmony_ci        uint32_t inputIndicesValues[2] = {0, 1};
37612e714ceSopenharmony_ci        uint32_t paramIndicesValues = 2;
37712e714ceSopenharmony_ci        uint32_t outputIndicesValues = 3;
37812e714ceSopenharmony_ci        OH_NN_UInt32Array paramIndices = {&paramIndicesValues, 1 * 4};
37912e714ceSopenharmony_ci        OH_NN_UInt32Array inputIndices = {inputIndicesValues, 2 * 4};
38012e714ceSopenharmony_ci        OH_NN_UInt32Array outputIndices = {&outputIndicesValues, 1 * 4};
38112e714ceSopenharmony_ci
38212e714ceSopenharmony_ci        // 向模型实例添加Add算子
38312e714ceSopenharmony_ci        returnCode = OH_NNModel_AddOperation(model, OH_NN_OPS_ADD, &paramIndices, &inputIndices, &outputIndices);
38412e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Add operation to model failed.");
38512e714ceSopenharmony_ci
38612e714ceSopenharmony_ci        // 设置模型实例的输入张量、输出张量的索引
38712e714ceSopenharmony_ci        returnCode = OH_NNModel_SpecifyInputsAndOutputs(model, &inputIndices, &outputIndices);
38812e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Specify model inputs and outputs failed.");
38912e714ceSopenharmony_ci
39012e714ceSopenharmony_ci        // 完成模型实例的构建
39112e714ceSopenharmony_ci        returnCode = OH_NNModel_Finish(model);
39212e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "Build model failed.");
39312e714ceSopenharmony_ci
39412e714ceSopenharmony_ci        // 返回模型实例
39512e714ceSopenharmony_ci        *pmodel = model;
39612e714ceSopenharmony_ci        return OH_NN_SUCCESS;
39712e714ceSopenharmony_ci    }
39812e714ceSopenharmony_ci    ```
39912e714ceSopenharmony_ci
40012e714ceSopenharmony_ci5. 查询Neural Network Runtime已经对接的AI加速芯片。
40112e714ceSopenharmony_ci
40212e714ceSopenharmony_ci    Neural Network Runtime支持通过HDI接口,对接多种AI加速芯片。在执行模型编译前,需要查询当前设备下,Neural Network Runtime已经对接的AI加速芯片。每个AI加速芯片对应唯一的ID值,在编译阶段需要通过设备ID,指定模型编译的芯片。
40312e714ceSopenharmony_ci    ```cpp
40412e714ceSopenharmony_ci    void GetAvailableDevices(std::vector<size_t>& availableDevice)
40512e714ceSopenharmony_ci    {
40612e714ceSopenharmony_ci        availableDevice.clear();
40712e714ceSopenharmony_ci
40812e714ceSopenharmony_ci        // 获取可用的硬件ID
40912e714ceSopenharmony_ci        const size_t* devices = nullptr;
41012e714ceSopenharmony_ci        uint32_t deviceCount = 0;
41112e714ceSopenharmony_ci        OH_NN_ReturnCode ret = OH_NNDevice_GetAllDevicesID(&devices, &deviceCount);
41212e714ceSopenharmony_ci        if (ret != OH_NN_SUCCESS) {
41312e714ceSopenharmony_ci            std::cout << "GetAllDevicesID failed, get no available device." << std::endl;
41412e714ceSopenharmony_ci            return;
41512e714ceSopenharmony_ci        }
41612e714ceSopenharmony_ci
41712e714ceSopenharmony_ci        for (uint32_t i = 0; i < deviceCount; i++) {
41812e714ceSopenharmony_ci            availableDevice.emplace_back(devices[i]);
41912e714ceSopenharmony_ci        }
42012e714ceSopenharmony_ci    }
42112e714ceSopenharmony_ci    ```
42212e714ceSopenharmony_ci
42312e714ceSopenharmony_ci6. 在指定的设备上编译模型。
42412e714ceSopenharmony_ci
42512e714ceSopenharmony_ci    Neural Network Runtime使用抽象的模型表达描述AI模型的拓扑结构。在AI加速芯片上执行前,需要通过Neural Network Runtime提供的编译模块来创建编译实例,并由编译实例将抽象的模型表达下发至芯片驱动层,转换成可以直接推理计算的格式,即模型编译。
42612e714ceSopenharmony_ci    ```cpp
42712e714ceSopenharmony_ci    OH_NN_ReturnCode CreateCompilation(OH_NNModel* model, const std::vector<size_t>& availableDevice,
42812e714ceSopenharmony_ci                                       OH_NNCompilation** pCompilation)
42912e714ceSopenharmony_ci    {
43012e714ceSopenharmony_ci        // 创建编译实例compilation,将构图的模型实例或MSLite传下来的模型实例传入
43112e714ceSopenharmony_ci        OH_NNCompilation* compilation = OH_NNCompilation_Construct(model);
43212e714ceSopenharmony_ci        CHECKEQ(compilation, nullptr, -1, "OH_NNCore_ConstructCompilationWithNNModel failed.");
43312e714ceSopenharmony_ci
43412e714ceSopenharmony_ci        // 设置编译的硬件、缓存路径、性能模式、计算优先级、是否开启float16低精度计算等选项
43512e714ceSopenharmony_ci        // 选择在第一个设备上编译模型
43612e714ceSopenharmony_ci        returnCode = OH_NNCompilation_SetDevice(compilation, availableDevice[0]);
43712e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNCompilation_SetDevice failed.");
43812e714ceSopenharmony_ci
43912e714ceSopenharmony_ci        // 将模型编译结果缓存在/data/local/tmp目录下,版本号指定为1
44012e714ceSopenharmony_ci        returnCode = OH_NNCompilation_SetCache(compilation, "/data/local/tmp", 1);
44112e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNCompilation_SetCache failed.");
44212e714ceSopenharmony_ci
44312e714ceSopenharmony_ci        // 设置硬件性能模式
44412e714ceSopenharmony_ci        returnCode = OH_NNCompilation_SetPerformanceMode(compilation, OH_NN_PERFORMANCE_EXTREME);
44512e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNCompilation_SetPerformanceMode failed.");
44612e714ceSopenharmony_ci
44712e714ceSopenharmony_ci        // 设置推理执行优先级
44812e714ceSopenharmony_ci        returnCode = OH_NNCompilation_SetPriority(compilation, OH_NN_PRIORITY_HIGH);
44912e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNCompilation_SetPriority failed.");
45012e714ceSopenharmony_ci
45112e714ceSopenharmony_ci        // 是否开启FP16计算模式
45212e714ceSopenharmony_ci        returnCode = OH_NNCompilation_EnableFloat16(compilation, false);
45312e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNCompilation_EnableFloat16 failed.");
45412e714ceSopenharmony_ci
45512e714ceSopenharmony_ci        // 执行模型编译
45612e714ceSopenharmony_ci        returnCode = OH_NNCompilation_Build(compilation);
45712e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNCompilation_Build failed.");
45812e714ceSopenharmony_ci
45912e714ceSopenharmony_ci        *pCompilation = compilation;
46012e714ceSopenharmony_ci        return OH_NN_SUCCESS;
46112e714ceSopenharmony_ci    }
46212e714ceSopenharmony_ci    ```
46312e714ceSopenharmony_ci
46412e714ceSopenharmony_ci7. 创建执行器。
46512e714ceSopenharmony_ci
46612e714ceSopenharmony_ci    完成模型编译后,需要调用Neural Network Runtime的执行模块,通过编译实例创建执行器。模型推理阶段中的设置模型输入、触发推理计算以及获取模型输出等操作均需要围绕执行器完成。
46712e714ceSopenharmony_ci    ```cpp
46812e714ceSopenharmony_ci    OH_NNExecutor* CreateExecutor(OH_NNCompilation* compilation)
46912e714ceSopenharmony_ci    {
47012e714ceSopenharmony_ci        // 通过编译实例compilation创建执行器executor
47112e714ceSopenharmony_ci        OH_NNExecutor *executor = OH_NNExecutor_Construct(compilation);
47212e714ceSopenharmony_ci        CHECKEQ(executor, nullptr, -1, "OH_NNExecutor_Construct failed.");
47312e714ceSopenharmony_ci        return executor;
47412e714ceSopenharmony_ci    }
47512e714ceSopenharmony_ci    ```
47612e714ceSopenharmony_ci
47712e714ceSopenharmony_ci8. 执行推理计算,并打印推理结果。
47812e714ceSopenharmony_ci
47912e714ceSopenharmony_ci    通过执行模块提供的接口,将推理计算所需要的输入数据传递给执行器,触发执行器完成一次推理计算,获取模型的推理结果并打印。
48012e714ceSopenharmony_ci    ```cpp
48112e714ceSopenharmony_ci    OH_NN_ReturnCode Run(OH_NNExecutor* executor, const std::vector<size_t>& availableDevice)
48212e714ceSopenharmony_ci    {
48312e714ceSopenharmony_ci        // 从executor获取输入输出信息
48412e714ceSopenharmony_ci        // 获取输入张量的个数
48512e714ceSopenharmony_ci        size_t inputCount = 0;
48612e714ceSopenharmony_ci        returnCode = OH_NNExecutor_GetInputCount(executor, &inputCount);
48712e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNExecutor_GetInputCount failed.");
48812e714ceSopenharmony_ci        std::vector<NN_TensorDesc*> inputTensorDescs;
48912e714ceSopenharmony_ci        NN_TensorDesc* tensorDescTmp = nullptr;
49012e714ceSopenharmony_ci        for (size_t i = 0; i < inputCount; ++i) {
49112e714ceSopenharmony_ci            // 创建输入张量的描述
49212e714ceSopenharmony_ci            tensorDescTmp = OH_NNExecutor_CreateInputTensorDesc(executor, i);
49312e714ceSopenharmony_ci            CHECKEQ(tensorDescTmp, nullptr, -1, "OH_NNExecutor_CreateInputTensorDesc failed.");
49412e714ceSopenharmony_ci            inputTensorDescs.emplace_back(tensorDescTmp);
49512e714ceSopenharmony_ci        }
49612e714ceSopenharmony_ci        // 获取输出张量的个数
49712e714ceSopenharmony_ci        size_t outputCount = 0;
49812e714ceSopenharmony_ci        returnCode = OH_NNExecutor_GetOutputCount(executor, &outputCount);
49912e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNExecutor_GetOutputCount failed.");
50012e714ceSopenharmony_ci        std::vector<NN_TensorDesc*> outputTensorDescs;
50112e714ceSopenharmony_ci        for (size_t i = 0; i < outputCount; ++i) {
50212e714ceSopenharmony_ci            // 创建输出张量的描述
50312e714ceSopenharmony_ci            tensorDescTmp = OH_NNExecutor_CreateOutputTensorDesc(executor, i);
50412e714ceSopenharmony_ci            CHECKEQ(tensorDescTmp, nullptr, -1, "OH_NNExecutor_CreateOutputTensorDesc failed.");
50512e714ceSopenharmony_ci            outputTensorDescs.emplace_back(tensorDescTmp);
50612e714ceSopenharmony_ci        }
50712e714ceSopenharmony_ci
50812e714ceSopenharmony_ci        // 创建输入和输出张量
50912e714ceSopenharmony_ci        NN_Tensor* inputTensors[inputCount];
51012e714ceSopenharmony_ci        NN_Tensor* tensor = nullptr;
51112e714ceSopenharmony_ci        for (size_t i = 0; i < inputCount; ++i) {
51212e714ceSopenharmony_ci            tensor = nullptr;
51312e714ceSopenharmony_ci            tensor = OH_NNTensor_Create(availableDevice[0], inputTensorDescs[i]);
51412e714ceSopenharmony_ci            CHECKEQ(tensor, nullptr, -1, "OH_NNTensor_Create failed.");
51512e714ceSopenharmony_ci            inputTensors[i] = tensor;
51612e714ceSopenharmony_ci        }
51712e714ceSopenharmony_ci        NN_Tensor* outputTensors[outputCount];
51812e714ceSopenharmony_ci        for (size_t i = 0; i < outputCount; ++i) {
51912e714ceSopenharmony_ci            tensor = nullptr;
52012e714ceSopenharmony_ci            tensor = OH_NNTensor_Create(availableDevice[0], outputTensorDescs[i]);
52112e714ceSopenharmony_ci            CHECKEQ(tensor, nullptr, -1, "OH_NNTensor_Create failed.");
52212e714ceSopenharmony_ci            outputTensors[i] = tensor;
52312e714ceSopenharmony_ci        }
52412e714ceSopenharmony_ci
52512e714ceSopenharmony_ci        // 设置输入张量的数据
52612e714ceSopenharmony_ci        returnCode = SetInputData(inputTensors, inputCount);
52712e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "SetInputData failed.");
52812e714ceSopenharmony_ci
52912e714ceSopenharmony_ci        // 执行推理
53012e714ceSopenharmony_ci        returnCode = OH_NNExecutor_RunSync(executor, inputTensors, inputCount, outputTensors, outputCount);
53112e714ceSopenharmony_ci        CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNExecutor_RunSync failed.");
53212e714ceSopenharmony_ci
53312e714ceSopenharmony_ci        // 打印输出张量的数据
53412e714ceSopenharmony_ci        Print(outputTensors, outputCount);
53512e714ceSopenharmony_ci
53612e714ceSopenharmony_ci        // 清理输入和输出张量以及张量描述
53712e714ceSopenharmony_ci        for (size_t i = 0; i < inputCount; ++i) {
53812e714ceSopenharmony_ci            returnCode = OH_NNTensor_Destroy(&inputTensors[i]);
53912e714ceSopenharmony_ci            CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNTensor_Destroy failed.");
54012e714ceSopenharmony_ci            returnCode = OH_NNTensorDesc_Destroy(&inputTensorDescs[i]);
54112e714ceSopenharmony_ci            CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNTensorDesc_Destroy failed.");
54212e714ceSopenharmony_ci        }
54312e714ceSopenharmony_ci        for (size_t i = 0; i < outputCount; ++i) {
54412e714ceSopenharmony_ci            returnCode = OH_NNTensor_Destroy(&outputTensors[i]);
54512e714ceSopenharmony_ci            CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNTensor_Destroy failed.");
54612e714ceSopenharmony_ci            returnCode = OH_NNTensorDesc_Destroy(&outputTensorDescs[i]);
54712e714ceSopenharmony_ci            CHECKNEQ(returnCode, OH_NN_SUCCESS, -1, "OH_NNTensorDesc_Destroy failed.");
54812e714ceSopenharmony_ci        }
54912e714ceSopenharmony_ci
55012e714ceSopenharmony_ci        return OH_NN_SUCCESS;
55112e714ceSopenharmony_ci    }
55212e714ceSopenharmony_ci    ```
55312e714ceSopenharmony_ci
55412e714ceSopenharmony_ci9. 构建端到端模型构造-编译-执行流程。
55512e714ceSopenharmony_ci
55612e714ceSopenharmony_ci    步骤4-步骤8实现了模型的模型构造、编译和执行流程,并封装成多个函数,便于模块化开发。以下示例代码将串联这些函数, 形成一个完整的Neural Network Runtime使用流程。
55712e714ceSopenharmony_ci    ```cpp
55812e714ceSopenharmony_ci    int main()
55912e714ceSopenharmony_ci    {
56012e714ceSopenharmony_ci        OH_NNModel* model = nullptr;
56112e714ceSopenharmony_ci        OH_NNCompilation* compilation = nullptr;
56212e714ceSopenharmony_ci        OH_NNExecutor* executor = nullptr;
56312e714ceSopenharmony_ci        std::vector<size_t> availableDevices;
56412e714ceSopenharmony_ci
56512e714ceSopenharmony_ci        // 模型构造
56612e714ceSopenharmony_ci        OH_NNModel* model = nullptr;
56712e714ceSopenharmony_ci        OH_NN_ReturnCode ret = BuildModel(&model);
56812e714ceSopenharmony_ci        if (ret != OH_NN_SUCCESS) {
56912e714ceSopenharmony_ci            std::cout << "BuildModel failed." << std::endl;
57012e714ceSopenharmony_ci            OH_NNModel_Destroy(&model);
57112e714ceSopenharmony_ci            return -1;
57212e714ceSopenharmony_ci        }
57312e714ceSopenharmony_ci
57412e714ceSopenharmony_ci        // 获取可执行的设备
57512e714ceSopenharmony_ci        GetAvailableDevices(availableDevices);
57612e714ceSopenharmony_ci        if (availableDevices.empty()) {
57712e714ceSopenharmony_ci            std::cout << "No available device." << std::endl;
57812e714ceSopenharmony_ci            OH_NNModel_Destroy(&model);
57912e714ceSopenharmony_ci            return -1;
58012e714ceSopenharmony_ci        }
58112e714ceSopenharmony_ci
58212e714ceSopenharmony_ci        // 模型编译
58312e714ceSopenharmony_ci        ret = CreateCompilation(model, availableDevices, &compilation);
58412e714ceSopenharmony_ci        if (ret != OH_NN_SUCCESS) {
58512e714ceSopenharmony_ci            std::cout << "CreateCompilation failed." << std::endl;
58612e714ceSopenharmony_ci            OH_NNModel_Destroy(&model);
58712e714ceSopenharmony_ci            OH_NNCompilation_Destroy(&compilation);
58812e714ceSopenharmony_ci            return -1;
58912e714ceSopenharmony_ci        }
59012e714ceSopenharmony_ci
59112e714ceSopenharmony_ci        // 销毁模型实例
59212e714ceSopenharmony_ci        OH_NNModel_Destroy(&model);
59312e714ceSopenharmony_ci
59412e714ceSopenharmony_ci        // 创建模型的推理执行器
59512e714ceSopenharmony_ci        executor = CreateExecutor(compilation);
59612e714ceSopenharmony_ci        if (executor == nullptr) {
59712e714ceSopenharmony_ci            std::cout << "CreateExecutor failed, no executor is created." << std::endl;
59812e714ceSopenharmony_ci            OH_NNCompilation_Destroy(&compilation);
59912e714ceSopenharmony_ci            return -1;
60012e714ceSopenharmony_ci        }
60112e714ceSopenharmony_ci
60212e714ceSopenharmony_ci        // 销毁编译实例
60312e714ceSopenharmony_ci        OH_NNCompilation_Destroy(&compilation);
60412e714ceSopenharmony_ci
60512e714ceSopenharmony_ci        // 使用上一步创建的执行器,执行推理计算
60612e714ceSopenharmony_ci        ret = Run(executor, availableDevices);
60712e714ceSopenharmony_ci        if (ret != OH_NN_SUCCESS) {
60812e714ceSopenharmony_ci            std::cout << "Run failed." << std::endl;
60912e714ceSopenharmony_ci            OH_NNExecutor_Destroy(&executor);
61012e714ceSopenharmony_ci            return -1;
61112e714ceSopenharmony_ci        }
61212e714ceSopenharmony_ci
61312e714ceSopenharmony_ci        // 销毁执行器实例
61412e714ceSopenharmony_ci        OH_NNExecutor_Destroy(&executor);
61512e714ceSopenharmony_ci
61612e714ceSopenharmony_ci        return 0;
61712e714ceSopenharmony_ci    }
61812e714ceSopenharmony_ci    ```
61912e714ceSopenharmony_ci
62012e714ceSopenharmony_ci## 调测验证
62112e714ceSopenharmony_ci
62212e714ceSopenharmony_ci1. 准备应用样例的编译配置文件。
62312e714ceSopenharmony_ci
62412e714ceSopenharmony_ci    新建一个 `CMakeLists.txt` 文件,为开发步骤中的应用样例文件 `nnrt_example.cpp` 添加编译配置。以下提供简单的 `CMakeLists.txt` 示例:
62512e714ceSopenharmony_ci    ```text
62612e714ceSopenharmony_ci    cmake_minimum_required(VERSION 3.16)
62712e714ceSopenharmony_ci    project(nnrt_example C CXX)
62812e714ceSopenharmony_ci
62912e714ceSopenharmony_ci    add_executable(nnrt_example
63012e714ceSopenharmony_ci        ./nnrt_example.cpp
63112e714ceSopenharmony_ci    )
63212e714ceSopenharmony_ci
63312e714ceSopenharmony_ci    target_link_libraries(nnrt_example
63412e714ceSopenharmony_ci        neural_network_runtime
63512e714ceSopenharmony_ci        neural_network_core
63612e714ceSopenharmony_ci    )
63712e714ceSopenharmony_ci    ```
63812e714ceSopenharmony_ci
63912e714ceSopenharmony_ci2. 编译应用样例。
64012e714ceSopenharmony_ci
64112e714ceSopenharmony_ci    执行以下命令,在当前目录下新建build/目录,在build/目录下编译 `nnrt_example.cpp`,得到二进制文件 `nnrt_example`。
64212e714ceSopenharmony_ci    ```shell
64312e714ceSopenharmony_ci    mkdir build && cd build
64412e714ceSopenharmony_ci    cmake -DCMAKE_TOOLCHAIN_FILE={交叉编译工具链的路径}/build/cmake/ohos.toolchain.cmake -DOHOS_ARCH=arm64-v8a -DOHOS_PLATFORM=OHOS -DOHOS_STL=c++_static ..
64512e714ceSopenharmony_ci    make
64612e714ceSopenharmony_ci    ```
64712e714ceSopenharmony_ci
64812e714ceSopenharmony_ci3. 执行以下代码,将样例推送到设备上执行。
64912e714ceSopenharmony_ci    ```shell
65012e714ceSopenharmony_ci    # 将编译得到的 `nnrt_example` 推送到设备上,执行样例。
65112e714ceSopenharmony_ci    hdc_std file send ./nnrt_example /data/local/tmp/.
65212e714ceSopenharmony_ci
65312e714ceSopenharmony_ci    # 给测试用例可执行文件加上权限。
65412e714ceSopenharmony_ci    hdc_std shell "chmod +x /data/local/tmp/nnrt_example"
65512e714ceSopenharmony_ci
65612e714ceSopenharmony_ci    # 执行测试用例
65712e714ceSopenharmony_ci    hdc_std shell "/data/local/tmp/nnrt_example"
65812e714ceSopenharmony_ci    ```
65912e714ceSopenharmony_ci
66012e714ceSopenharmony_ci    如果样例执行正常,应该得到以下输出。
66112e714ceSopenharmony_ci    ```text
66212e714ceSopenharmony_ci    Output index: 0, value is: 0.000000.
66312e714ceSopenharmony_ci    Output index: 1, value is: 2.000000.
66412e714ceSopenharmony_ci    Output index: 2, value is: 4.000000.
66512e714ceSopenharmony_ci    Output index: 3, value is: 6.000000.
66612e714ceSopenharmony_ci    Output index: 4, value is: 8.000000.
66712e714ceSopenharmony_ci    Output index: 5, value is: 10.000000.
66812e714ceSopenharmony_ci    Output index: 6, value is: 12.000000.
66912e714ceSopenharmony_ci    Output index: 7, value is: 14.000000.
67012e714ceSopenharmony_ci    Output index: 8, value is: 16.000000.
67112e714ceSopenharmony_ci    Output index: 9, value is: 18.000000.
67212e714ceSopenharmony_ci    Output index: 10, value is: 20.000000.
67312e714ceSopenharmony_ci    Output index: 11, value is: 22.000000.
67412e714ceSopenharmony_ci    ```
67512e714ceSopenharmony_ci
67612e714ceSopenharmony_ci4. 检查模型缓存(可选)。
67712e714ceSopenharmony_ci
67812e714ceSopenharmony_ci    如果在调测环境下,Neural Network Runtime对接的HDI服务支持模型缓存功能,执行完 `nnrt_example`, 可以在 `/data/local/tmp` 目录下找到生成的缓存文件。
67912e714ceSopenharmony_ci
68012e714ceSopenharmony_ci    > **说明:** 
68112e714ceSopenharmony_ci    >
68212e714ceSopenharmony_ci    > 模型的IR需要传递到硬件驱动层,由HDI服务将统一的IR图,编译成硬件专用的计算图,编译的过程非常耗时。Neural Network Runtime支持计算图缓存的特性,可以将HDI服务编译生成的计算图,缓存到设备存储中。当下一次在同一个加速芯片上编译同一个模型时,通过指定缓存的路径,Neural Network Runtime可以直接加载缓存文件中的计算图,减少编译消耗的时间。
68312e714ceSopenharmony_ci
68412e714ceSopenharmony_ci    检查缓存目录下的缓存文件:
68512e714ceSopenharmony_ci    ```shell
68612e714ceSopenharmony_ci    ls /data/local/tmp
68712e714ceSopenharmony_ci    ```
68812e714ceSopenharmony_ci
68912e714ceSopenharmony_ci    以下为打印结果:
69012e714ceSopenharmony_ci    ```text
69112e714ceSopenharmony_ci    # 0.nncache 1.nncache 2.nncache cache_info.nncache
69212e714ceSopenharmony_ci    ```
69312e714ceSopenharmony_ci
69412e714ceSopenharmony_ci    如果缓存不再使用,需要手动删除缓存,可以参考以下命令,删除缓存文件。
69512e714ceSopenharmony_ci    ```shell
69612e714ceSopenharmony_ci    rm /data/local/tmp/*nncache
69712e714ceSopenharmony_ci    ```