Neural Network Runtime对接AI推理框架开发指导
==================================

#### 场景介绍

Neural Network Runtime作为AI推理引擎和加速芯片的桥梁，为AI推理引擎提供精简的Native接口，满足推理引擎通过加速芯片执行端到端推理的需求。

本文以图1展示的Add单算子模型为例，介绍Neural Network Runtime的开发流程。Add算子包含两个输入、一个参数和一个输出，其中的activation参数用于指定Add算子中激活函数的类型。

**图1** Add单算子网络示意图

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/bd/v3/5Qbbg6EgSYaEJL4WMfs8Tw/zh-cn_image_0000002625109175.png?HW-CC-KV=V1&HW-CC-Date=20260612T073804Z&HW-CC-Expire=86400&HW-CC-Sign=F6290A4FCD38C5EFD46999F927B6212405E39F1DC5EEA4C9F5E9041B09309576)

#### 环境准备

#### \[h2\]环境要求

Neural Network Runtime部件的环境要求如下：

*   开发环境：Ubuntu 18.04及以上。
*   接入设备：系统定义的标准设备，系统中内置AI硬件驱动并已接入Neural Network Runtime。

由于Neural Network Runtime通过Native API对外开放，需要下载对应的SDK并通过Native开发套件编译Neural Network Runtime应用。可以使用DevEco Studio来搭建环境和编译代码。

#### \[h2\]环境搭建

1.  使用Ubuntu编译服务器的终端。
2.  指定native工具链路径来编译代码，可以使用DevEco Studio来下载对应的SDK来进行编译。
3.  DevEco Studio安装目录下的SDK路径可以在DevEco Studio工程界面，点击File > Settings... > 在settings中搜索SDK，下载对应的SDK即可。

#### 接口说明

以下为Neural Network Runtime开发流程中的常用接口，具体可见[NeuralNetworkRuntime](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-neuralnetworkruntime)。

#### \[h2\]结构体

| 结构体名称 | 描述 |
| :-- | :-- |
| typedef struct OH\_NNModel OH\_NNModel | Neural Network Runtime的模型句柄，用于构造模型。 |
| typedef struct OH\_NNCompilation OH\_NNCompilation | Neural Network Runtime的编译器句柄，用于编译AI模型。 |
| typedef struct OH\_NNExecutor OH\_NNExecutor | Neural Network Runtime的执行器句柄，用于在指定设备上执行推理计算。 |
| typedef struct NN\_QuantParam NN\_QuantParam | Neural Network Runtime的量化参数句柄，用于在构造模型时指定张量的量化参数。 |
| typedef struct NN\_TensorDesc NN\_TensorDesc | Neural Network Runtime的张量描述句柄，用于描述张量的各类属性，例如数据布局、数据类型、形状等。 |
| typedef struct NN\_Tensor NN\_Tensor | Neural Network Runtime的张量句柄，用于设置执行器的推理输入和输出张量。 |

#### \[h2\]模型构造接口

| 接口名称 | 描述 |
| :-- | :-- |
| OH\_NNModel\_Construct() | 创建OH\_NNModel类型的模型实例。 |
| OH\_NN\_ReturnCode OH\_NNModel\_AddTensorToModel(OH\_NNModel \*model, const NN\_TensorDesc \*tensorDesc) | 向模型实例中添加张量。 |
| OH\_NN\_ReturnCode OH\_NNModel\_SetTensorData(OH\_NNModel \*model, uint32\_t index, const void \*dataBuffer, size\_t length) | 设置张量的数值。 |
| 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) | 向模型实例中添加算子。 |
| OH\_NN\_ReturnCode OH\_NNModel\_SpecifyInputsAndOutputs(OH\_NNModel \*model, const OH\_NN\_UInt32Array \*inputIndices, const OH\_NN\_UInt32Array \*outputIndices) | 指定模型的输入和输出张量的索引值。 |
| OH\_NN\_ReturnCode OH\_NNModel\_Finish(OH\_NNModel \*model) | 完成模型构图。 |
| void OH\_NNModel\_Destroy(OH\_NNModel \*\*model) | 销毁模型实例。 |

#### \[h2\]模型编译接口

| 接口名称 | 描述 |
| :-- | :-- |
| OH\_NNCompilation \*OH\_NNCompilation\_Construct(const OH\_NNModel \*model) | 基于模型实例创建OH\_NNCompilation类型的编译实例。 |
| OH\_NNCompilation \*OH\_NNCompilation\_ConstructWithOfflineModelFile(const char \*modelPath) | 基于离线模型文件路径创建OH\_NNCompilation类型的编译实例。 |
| OH\_NNCompilation \*OH\_NNCompilation\_ConstructWithOfflineModelBuffer(const void \*modelBuffer, size\_t modelSize) | 基于离线模型文件内存创建OH\_NNCompilation类型的编译实例。 |
| OH\_NNCompilation \*OH\_NNCompilation\_ConstructForCache() | 创建一个空的编译实例，以便稍后从模型缓存中恢复。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_ExportCacheToBuffer(OH\_NNCompilation \*compilation, const void \*buffer, size\_t length, size\_t \*modelSize) | 将模型缓存写入到指定内存区域。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_ImportCacheFromBuffer(OH\_NNCompilation \*compilation, const void \*buffer, size\_t modelSize) | 从指定内存区域读取模型缓存。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_AddExtensionConfig(OH\_NNCompilation \*compilation, const char \*configName, const void \*configValue, const size\_t configValueSize) | 为自定义硬件属性添加扩展配置，具体硬件的扩展属性名称和属性值需要从硬件厂商的文档中获取。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_SetDevice(OH\_NNCompilation \*compilation, size\_t deviceID) | 指定模型编译和计算的硬件，可通过设备管理接口获取。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_SetCache(OH\_NNCompilation \*compilation, const char \*cachePath, uint32\_t version) | 设置编译模型的缓存目录和版本。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_SetPerformanceMode(OH\_NNCompilation \*compilation, OH\_NN\_PerformanceMode performanceMode) | 设置模型计算的性能模式。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_SetPriority(OH\_NNCompilation \*compilation, OH\_NN\_Priority priority) | 设置模型计算的优先级。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_EnableFloat16(OH\_NNCompilation \*compilation, bool enableFloat16) | 是否以float16的浮点数精度计算。 |
| OH\_NN\_ReturnCode OH\_NNCompilation\_Build(OH\_NNCompilation \*compilation) | 执行模型编译。 |
| void OH\_NNCompilation\_Destroy(OH\_NNCompilation \*\*compilation) | 销毁编译实例。 |

#### \[h2\]张量描述接口

| 接口名称 | 描述 |
| :-- | :-- |
| NN\_TensorDesc \*OH\_NNTensorDesc\_Create() | 创建一个张量描述实例，用于后续创建张量。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_SetName(NN\_TensorDesc \*tensorDesc, const char \*name) | 设置张量描述的名称。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_GetName(const NN\_TensorDesc \*tensorDesc, const char \*\*name) | 获取张量描述的名称。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_SetDataType(NN\_TensorDesc \*tensorDesc, OH\_NN\_DataType dataType) | 设置张量描述的数据类型。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_GetDataType(const NN\_TensorDesc \*tensorDesc, OH\_NN\_DataType \*dataType) | 获取张量描述的数据类型。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_SetShape(NN\_TensorDesc \*tensorDesc, const int32\_t \*shape, size\_t shapeLength) | 设置张量描述的形状。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_GetShape(const NN\_TensorDesc \*tensorDesc, int32\_t \*\*shape, size\_t \*shapeLength) | 获取张量描述的形状。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_SetFormat(NN\_TensorDesc \*tensorDesc, OH\_NN\_Format format) | 设置张量描述的数据布局。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_GetFormat(const NN\_TensorDesc \*tensorDesc, OH\_NN\_Format \*format) | 获取张量描述的数据布局。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_GetElementCount(const NN\_TensorDesc \*tensorDesc, size\_t \*elementCount) | 获取张量描述的元素个数。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_GetByteSize(const NN\_TensorDesc \*tensorDesc, size\_t \*byteSize) | 获取基于张量描述的形状和数据类型计算的数据占用字节数。 |
| OH\_NN\_ReturnCode OH\_NNTensorDesc\_Destroy(NN\_TensorDesc \*\*tensorDesc) | 销毁张量描述实例。 |

#### \[h2\]张量接口

| 接口名称 | 描述 |
| :-- | :-- |
| NN\_Tensor\* OH\_NNTensor\_Create(size\_t deviceID, NN\_TensorDesc \*tensorDesc) | 从张量描述创建张量实例，会申请设备共享内存。 |
| NN\_Tensor\* OH\_NNTensor\_CreateWithSize(size\_t deviceID, NN\_TensorDesc \*tensorDesc, size\_t size) | 按照指定内存大小和张量描述创建张量实例，会申请设备共享内存。 |
| NN\_Tensor\* OH\_NNTensor\_CreateWithFd(size\_t deviceID, NN\_TensorDesc \*tensorDesc, int fd, size\_t size, size\_t offset) | 按照指定共享内存的文件描述符和张量描述创建张量实例，从而可以复用其他张量的设备共享内存。 |
| NN\_TensorDesc\* OH\_NNTensor\_GetTensorDesc(const NN\_Tensor \*tensor) | 获取张量内部的张量描述实例指针，从而可读取张量的属性，例如数据类型、形状等。 |
| void\* OH\_NNTensor\_GetDataBuffer(const NN\_Tensor \*tensor) | 获取张量数据的内存地址，可以读写张量数据。 |
| OH\_NN\_ReturnCode OH\_NNTensor\_GetFd(const NN\_Tensor \*tensor, int \*fd) | 获取张量数据所在共享内存的文件描述符，文件描述符fd对应了一块设备共享内存。 |
| OH\_NN\_ReturnCode OH\_NNTensor\_GetSize(const NN\_Tensor \*tensor, size\_t \*size) | 获取张量数据所在共享内存的大小。 |
| OH\_NN\_ReturnCode OH\_NNTensor\_GetOffset(const NN\_Tensor \*tensor, size\_t \*offset) | 获取张量数据所在共享内存上的偏移量，张量数据可使用的大小为所在共享内存的大小减去偏移量。 |
| OH\_NN\_ReturnCode OH\_NNTensor\_Destroy(NN\_Tensor \*\*tensor) | 销毁张量实例。 |

#### \[h2\]执行推理接口

| 接口名称 | 描述 |
| :-- | :-- |
| OH\_NNExecutor \*OH\_NNExecutor\_Construct(OH\_NNCompilation \*compilation) | 创建OH\_NNExecutor类型的执行器实例。 |
| OH\_NN\_ReturnCode OH\_NNExecutor\_GetOutputShape(OH\_NNExecutor \*executor, uint32\_t outputIndex, int32\_t \*\*shape, uint32\_t \*shapeLength) | 获取输出张量的维度信息，用于输出张量具有动态形状的情况。 |
| OH\_NN\_ReturnCode OH\_NNExecutor\_GetInputCount(const OH\_NNExecutor \*executor, size\_t \*inputCount) | 获取输入张量的数量。 |
| OH\_NN\_ReturnCode OH\_NNExecutor\_GetOutputCount(const OH\_NNExecutor \*executor, size\_t \*outputCount) | 获取输出张量的数量。 |
| NN\_TensorDesc\* OH\_NNExecutor\_CreateInputTensorDesc(const OH\_NNExecutor \*executor, size\_t index) | 由指定索引值创建一个输入张量的描述，用于读取张量的属性或创建张量实例。 |
| NN\_TensorDesc\* OH\_NNExecutor\_CreateOutputTensorDesc(const OH\_NNExecutor \*executor, size\_t index) | 由指定索引值创建一个输出张量的描述，用于读取张量的属性或创建张量实例。 |
| OH\_NN\_ReturnCode OH\_NNExecutor\_GetInputDimRange(const OH\_NNExecutor \*executor, size\_t index, size\_t \*\*minInputDims, size\_t \*\*maxInputDims, size\_t \*shapeLength) | 获取所有输入张量的维度范围。当输入张量具有动态形状时，不同设备可能支持不同的维度范围。 |
| OH\_NN\_ReturnCode OH\_NNExecutor\_SetOnRunDone(OH\_NNExecutor \*executor, NN\_OnRunDone onRunDone) | 设置异步推理结束后的回调处理函数，回调函数定义详见接口文档。 |
| OH\_NN\_ReturnCode OH\_NNExecutor\_SetOnServiceDied(OH\_NNExecutor \*executor, NN\_OnServiceDied onServiceDied) | 设置异步推理执行期间设备驱动服务突然死亡时的回调处理函数，回调函数定义详见接口文档。 |
| OH\_NN\_ReturnCode OH\_NNExecutor\_RunSync(OH\_NNExecutor \*executor, NN\_Tensor \*inputTensor\[\], size\_t inputCount, NN\_Tensor \*outputTensor\[\], size\_t outputCount) | 执行同步推理。 |
| 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) | 执行异步推理。 |
| void OH\_NNExecutor\_Destroy(OH\_NNExecutor \*\*executor) | 销毁执行器实例。 |

#### \[h2\]设备管理接口

| 接口名称 | 描述 |
| :-- | :-- |
| OH\_NN\_ReturnCode OH\_NNDevice\_GetAllDevicesID(const size\_t \*\*allDevicesID, uint32\_t \*deviceCount) | 获取对接到Neural Network Runtime的所有硬件ID。 |
| OH\_NN\_ReturnCode OH\_NNDevice\_GetName(size\_t deviceID, const char \*\*name) | 获取指定硬件的名称。 |
| OH\_NN\_ReturnCode OH\_NNDevice\_GetType(size\_t deviceID, OH\_NN\_DeviceType \*deviceType) | 获取指定硬件的类别信息。 |

#### 开发步骤

Neural Network Runtime的开发流程主要包含**模型构造**、**模型编译**和**推理执行**三个阶段。以下开发步骤以Add单算子模型为例，介绍调用Neural Network Runtime接口，开发应用的过程。

1.  创建应用样例文件。
    
    首先，创建Neural Network Runtime应用样例的源文件。在项目目录下执行以下命令，创建nnrt\_example/目录，并在目录下创建 nnrt\_example.cpp 源文件。
    
    mkdir ~/nnrt\_example && cd ~/nnrt\_example
    touch nnrt\_example.cpp
    
2.  导入Neural Network Runtime。
    
    在 nnrt\_example.cpp 文件的开头添加以下代码，引入Neural Network Runtime。
    
    #include <iostream>
    #include <cstdarg>
    #include "neural\_network\_runtime/neural\_network\_runtime.h"
    
3.  定义日志打印、设置输入数据、数据打印等辅助函数。
    
    // 返回值检查宏
    #define CHECKNEQ(realRet, expectRet, retValue, ...) \\
        do { \\
            if ((realRet) != (expectRet)) { \\
                printf(\_\_VA\_ARGS\_\_); \\
                return (retValue); \\
            } \\
        } while (0)
    
    #define CHECKEQ(realRet, expectRet, retValue, ...) \\
        do { \\
            if ((realRet) == (expectRet)) { \\
                printf(\_\_VA\_ARGS\_\_); \\
                return (retValue); \\
            } \\
        } while (0)
    
    // 设置输入数据用于推理
    OH\_NN\_ReturnCode SetInputData(NN\_Tensor\* inputTensor\[\], size\_t inputSize)
    {
        OH\_NN\_DataType dataType(OH\_NN\_FLOAT32);
        OH\_NN\_ReturnCode ret{OH\_NN\_FAILED};
        size\_t elementCount = 0;
        for (size\_t i = 0; i < inputSize; ++i) {
            // 获取张量的数据内存
            auto data = OH\_NNTensor\_GetDataBuffer(inputTensor\[i\]);
            CHECKEQ(data, nullptr, OH\_NN\_FAILED, "Failed to get data buffer.");
            // 获取张量的描述
            auto desc = OH\_NNTensor\_GetTensorDesc(inputTensor\[i\]);
            CHECKEQ(desc, nullptr, OH\_NN\_FAILED, "Failed to get desc.");
            // 获取张量的数据类型
            ret = OH\_NNTensorDesc\_GetDataType(desc, &dataType);
            CHECKNEQ(ret, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Failed to get data type.");
            // 获取张量的元素个数
            ret = OH\_NNTensorDesc\_GetElementCount(desc, &elementCount);
            CHECKNEQ(ret, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Failed to get element count.");
            switch(dataType) {
                case OH\_NN\_FLOAT32: {
                    float\* floatValue = reinterpret\_cast<float\*>(data);
                    for (size\_t j = 0; j < elementCount; ++j) {
                        floatValue\[j\] = static\_cast<float>(j);
                    }
                    break;
                }
                case OH\_NN\_INT32: {
                    int\* intValue = reinterpret\_cast<int\*>(data);
                    for (size\_t j = 0; j < elementCount; ++j) {
                        intValue\[j\] = static\_cast<int>(j);
                    }
                    break;
                }
                default:
                    return OH\_NN\_FAILED;
            }
        }
        return OH\_NN\_SUCCESS;
    }
    
    OH\_NN\_ReturnCode Print(NN\_Tensor\* outputTensor\[\], size\_t outputSize)
    {
        OH\_NN\_DataType dataType(OH\_NN\_FLOAT32);
        OH\_NN\_ReturnCode ret{OH\_NN\_FAILED};
        size\_t elementCount = 0;
        for (size\_t i = 0; i < outputSize; ++i) {
            auto data = OH\_NNTensor\_GetDataBuffer(outputTensor\[i\]);
            CHECKEQ(data, nullptr, OH\_NN\_FAILED, "Failed to get data buffer.");
            auto desc = OH\_NNTensor\_GetTensorDesc(outputTensor\[i\]);
            CHECKEQ(desc, nullptr, OH\_NN\_FAILED, "Failed to get desc.");
            ret = OH\_NNTensorDesc\_GetDataType(desc, &dataType);
            CHECKNEQ(ret, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Failed to get data type.");
            ret = OH\_NNTensorDesc\_GetElementCount(desc, &elementCount);
            CHECKNEQ(ret, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Failed to get element count.");
            switch(dataType) {
                case OH\_NN\_FLOAT32: {
                    float\* floatValue = reinterpret\_cast<float\*>(data);
                    for (size\_t j = 0; j < elementCount; ++j) {
                        std::cout << "Output index: " << j << ", value is: " << floatValue\[j\] << "." << std::endl;
                    }
                    break;
                }
                case OH\_NN\_INT32: {
                    int\* intValue = reinterpret\_cast<int\*>(data);
                    for (size\_t j = 0; j < elementCount; ++j) {
                        std::cout << "Output index: " << j << ", value is: " << intValue\[j\] << "." << std::endl;
                    }
                    break;
                }
                default:
                    return OH\_NN\_FAILED;
            }
        }
    
        return OH\_NN\_SUCCESS;
    }
    
4.  构造模型。
    
    使用Neural Network Runtime的模型构造接口，构造Add单算子样例模型。
    
    OH\_NN\_ReturnCode BuildModel(OH\_NNModel\*\* pmodel)
    {
        // 创建模型实例model，进行模型构造
        OH\_NNModel\* model = OH\_NNModel\_Construct();
        CHECKEQ(model, nullptr, OH\_NN\_FAILED, "Create model failed.");
    
        // 添加Add算子的第一个输入张量，类型为float32，张量形状为\[1, 2, 2, 3\]
        NN\_TensorDesc\* tensorDesc = OH\_NNTensorDesc\_Create();
        CHECKEQ(tensorDesc, nullptr, OH\_NN\_FAILED, "Create TensorDesc failed.");
    
        int32\_t inputDims\[4\] = {1, 2, 2, 3};
        auto returnCode = OH\_NNTensorDesc\_SetShape(tensorDesc, inputDims, 4);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc shape failed.");
    
        returnCode = OH\_NNTensorDesc\_SetDataType(tensorDesc, OH\_NN\_FLOAT32);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc data type failed.");
    
        returnCode = OH\_NNTensorDesc\_SetFormat(tensorDesc, OH\_NN\_FORMAT\_NONE);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc format failed.");
    
        returnCode = OH\_NNModel\_AddTensorToModel(model, tensorDesc);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Add first TensorDesc to model failed.");
    
        returnCode = OH\_NNModel\_SetTensorType(model, 0, OH\_NN\_TENSOR);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set model tensor type failed.");
    
        // 添加Add算子的第二个输入张量，类型为float32，张量形状为\[1, 2, 2, 3\]
        tensorDesc = OH\_NNTensorDesc\_Create();
        CHECKEQ(tensorDesc, nullptr, OH\_NN\_FAILED, "Create TensorDesc failed.");
    
        returnCode = OH\_NNTensorDesc\_SetShape(tensorDesc, inputDims, 4);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc shape failed.");
    
        returnCode = OH\_NNTensorDesc\_SetDataType(tensorDesc, OH\_NN\_FLOAT32);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc data type failed.");
    
        returnCode = OH\_NNTensorDesc\_SetFormat(tensorDesc, OH\_NN\_FORMAT\_NONE);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc format failed.");
    
        returnCode = OH\_NNModel\_AddTensorToModel(model, tensorDesc);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Add second TensorDesc to model failed.");
    
        returnCode = OH\_NNModel\_SetTensorType(model, 1, OH\_NN\_TENSOR);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set model tensor type failed.");
    
        // 添加Add算子的参数张量，该参数张量用于指定激活函数的类型，张量的数据类型为int8。
        tensorDesc = OH\_NNTensorDesc\_Create();
        CHECKEQ(tensorDesc, nullptr, OH\_NN\_FAILED, "Create TensorDesc failed.");
    
        int32\_t activationDims = 1;
        returnCode = OH\_NNTensorDesc\_SetShape(tensorDesc, &activationDims, 1);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc shape failed.");
    
        returnCode = OH\_NNTensorDesc\_SetDataType(tensorDesc, OH\_NN\_INT8);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc data type failed.");
    
        returnCode = OH\_NNTensorDesc\_SetFormat(tensorDesc, OH\_NN\_FORMAT\_NONE);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc format failed.");
    
        returnCode = OH\_NNModel\_AddTensorToModel(model, tensorDesc);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Add second TensorDesc to model failed.");
    
        returnCode = OH\_NNModel\_SetTensorType(model, 2, OH\_NN\_ADD\_ACTIVATIONTYPE);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set model tensor type failed.");
    
        // 将激活函数类型设置为OH\_NN\_FUSED\_NONE，表示该算子不添加激活函数。
        int8\_t activationValue = OH\_NN\_FUSED\_NONE;
        returnCode = OH\_NNModel\_SetTensorData(model, 2, &activationValue, sizeof(int8\_t));
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set model tensor data failed.");
    
        // 设置Add算子的输出张量，类型为float32，张量形状为\[1, 2, 2, 3\]
        tensorDesc = OH\_NNTensorDesc\_Create();
        CHECKEQ(tensorDesc, nullptr, OH\_NN\_FAILED, "Create TensorDesc failed.");
    
        returnCode = OH\_NNTensorDesc\_SetShape(tensorDesc, inputDims, 4);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc shape failed.");
    
        returnCode = OH\_NNTensorDesc\_SetDataType(tensorDesc, OH\_NN\_FLOAT32);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc data type failed.");
    
        returnCode = OH\_NNTensorDesc\_SetFormat(tensorDesc, OH\_NN\_FORMAT\_NONE);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set TensorDesc format failed.");
    
        returnCode = OH\_NNModel\_AddTensorToModel(model, tensorDesc);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Add forth TensorDesc to model failed.");
    
        returnCode = OH\_NNModel\_SetTensorType(model, 3, OH\_NN\_TENSOR);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Set model tensor type failed.");
    
        // 指定Add算子的输入张量、参数张量和输出张量的索引
        uint32\_t inputIndicesValues\[2\] = {0, 1};
        uint32\_t paramIndicesValues = 2;
        uint32\_t outputIndicesValues = 3;
        OH\_NN\_UInt32Array paramIndices = {&paramIndicesValues, 1};
        OH\_NN\_UInt32Array inputIndices = {inputIndicesValues, 2};
        OH\_NN\_UInt32Array outputIndices = {&outputIndicesValues, 1};
    
        // 向模型实例添加Add算子
        returnCode = OH\_NNModel\_AddOperation(model, OH\_NN\_OPS\_ADD, &paramIndices, &inputIndices, &outputIndices);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Add operation to model failed.");
    
        // 设置模型实例的输入张量、输出张量的索引
        returnCode = OH\_NNModel\_SpecifyInputsAndOutputs(model, &inputIndices, &outputIndices);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Specify model inputs and outputs failed.");
    
        // 完成模型实例的构建
        returnCode = OH\_NNModel\_Finish(model);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "Build model failed.");
    
        // 返回模型实例
        \*pmodel = model;
        return OH\_NN\_SUCCESS;
    }
    
5.  查询Neural Network Runtime已经对接的AI加速芯片。
    
    Neural Network Runtime支持通过HDI接口，对接多种AI加速芯片。在执行模型编译前，需要查询当前设备下，Neural Network Runtime已经对接的AI加速芯片。每个AI加速芯片对应唯一的ID值，在编译阶段需要通过设备ID，指定模型编译的芯片。
    
    void GetAvailableDevices(std::vector<size\_t>& availableDevice)
    {
        availableDevice.clear();
    
        // 获取可用的硬件ID
        const size\_t\* devices = nullptr;
        uint32\_t deviceCount = 0;
        OH\_NN\_ReturnCode ret = OH\_NNDevice\_GetAllDevicesID(&devices, &deviceCount);
        if (ret != OH\_NN\_SUCCESS) {
            std::cout << "GetAllDevicesID failed, get no available device." << std::endl;
            return;
        }
    
        for (uint32\_t i = 0; i < deviceCount; i++) {
            availableDevice.emplace\_back(devices\[i\]);
        }
    }
    
6.  在指定的设备上编译模型。
    
    Neural Network Runtime使用抽象的模型表达描述AI模型的拓扑结构。在AI加速芯片上执行前，需要通过Neural Network Runtime提供的编译模块来创建编译实例，并由编译实例将抽象的模型表达下发至芯片驱动层，转换成可以直接推理计算的格式，即模型编译。
    
    OH\_NN\_ReturnCode CreateCompilation(OH\_NNModel\* model, const std::vector<size\_t>& availableDevice,
                                       OH\_NNCompilation\*\* pCompilation)
    {
        // 创建编译实例compilation，将构图的模型实例或MSLite传下来的模型实例传入
        OH\_NNCompilation\* compilation = OH\_NNCompilation\_Construct(model);
        CHECKEQ(compilation, nullptr, OH\_NN\_FAILED, "OH\_NNCore\_ConstructCompilationWithNNModel failed.");
    
        // 设置编译的硬件、缓存路径、性能模式、计算优先级、是否开启float16低精度计算等选项
        // 选择在第一个设备上编译模型
        auto returnCode = OH\_NNCompilation\_SetDevice(compilation, availableDevice\[0\]);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNCompilation\_SetDevice failed.");
    
        // 将模型编译结果缓存在/data/local/tmp目录下，版本号指定为1
        returnCode = OH\_NNCompilation\_SetCache(compilation, "/data/local/tmp", 1);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNCompilation\_SetCache failed.");
    
        // 设置硬件性能模式
        returnCode = OH\_NNCompilation\_SetPerformanceMode(compilation, OH\_NN\_PERFORMANCE\_EXTREME);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNCompilation\_SetPerformanceMode failed.");
    
        // 设置推理执行优先级
        returnCode = OH\_NNCompilation\_SetPriority(compilation, OH\_NN\_PRIORITY\_HIGH);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNCompilation\_SetPriority failed.");
    
        // 是否开启FP16计算模式
        returnCode = OH\_NNCompilation\_EnableFloat16(compilation, false);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNCompilation\_EnableFloat16 failed.");
    
        // 执行模型编译
        returnCode = OH\_NNCompilation\_Build(compilation);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNCompilation\_Build failed.");
    
        \*pCompilation = compilation;
        return OH\_NN\_SUCCESS;
    }
    
7.  创建执行器。
    
    完成模型编译后，需要调用Neural Network Runtime的执行模块，通过编译实例创建执行器。模型推理阶段中的设置模型输入、触发推理计算以及获取模型输出等操作均需要围绕执行器完成。
    
    OH\_NNExecutor\* CreateExecutor(OH\_NNCompilation\* compilation)
    {
        // 通过编译实例compilation创建执行器executor
        OH\_NNExecutor \*executor = OH\_NNExecutor\_Construct(compilation);
        CHECKEQ(executor, nullptr, nullptr, "OH\_NNExecutor\_Construct failed.");
        return executor;
    }
    
8.  执行推理计算，并打印推理结果。
    
    通过执行模块提供的接口，将推理计算所需要的输入数据传递给执行器，触发执行器完成一次推理计算，获取模型的推理结果并打印。
    
    OH\_NN\_ReturnCode Run(OH\_NNExecutor\* executor, const std::vector<size\_t>& availableDevice)
    {
        // 从executor获取输入输出信息
        // 获取输入张量的个数
        size\_t inputCount = 0;
        auto returnCode = OH\_NNExecutor\_GetInputCount(executor, &inputCount);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNExecutor\_GetInputCount failed.");
        std::vector<NN\_TensorDesc\*> inputTensorDescs;
        NN\_TensorDesc\* tensorDescTmp = nullptr;
        for (size\_t i = 0; i < inputCount; ++i) {
            // 创建输入张量的描述
            tensorDescTmp = OH\_NNExecutor\_CreateInputTensorDesc(executor, i);
            CHECKEQ(tensorDescTmp, nullptr, OH\_NN\_FAILED, "OH\_NNExecutor\_CreateInputTensorDesc failed.");
            inputTensorDescs.emplace\_back(tensorDescTmp);
        }
        // 获取输出张量的个数
        size\_t outputCount = 0;
        returnCode = OH\_NNExecutor\_GetOutputCount(executor, &outputCount);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNExecutor\_GetOutputCount failed.");
        std::vector<NN\_TensorDesc\*> outputTensorDescs;
        for (size\_t i = 0; i < outputCount; ++i) {
            // 创建输出张量的描述
            tensorDescTmp = OH\_NNExecutor\_CreateOutputTensorDesc(executor, i);
            CHECKEQ(tensorDescTmp, nullptr, OH\_NN\_FAILED, "OH\_NNExecutor\_CreateOutputTensorDesc failed.");
            outputTensorDescs.emplace\_back(tensorDescTmp);
        }
    
        // 创建输入和输出张量
        NN\_Tensor\* inputTensors\[inputCount\];
        NN\_Tensor\* tensor = nullptr;
        for (size\_t i = 0; i < inputCount; ++i) {
            tensor = nullptr;
            tensor = OH\_NNTensor\_Create(availableDevice\[0\], inputTensorDescs\[i\]);
            CHECKEQ(tensor, nullptr, OH\_NN\_FAILED, "OH\_NNTensor\_Create failed.");
            inputTensors\[i\] = tensor;
        }
        NN\_Tensor\* outputTensors\[outputCount\];
        for (size\_t i = 0; i < outputCount; ++i) {
            tensor = nullptr;
            tensor = OH\_NNTensor\_Create(availableDevice\[0\], outputTensorDescs\[i\]);
            CHECKEQ(tensor, nullptr, OH\_NN\_FAILED, "OH\_NNTensor\_Create failed.");
            outputTensors\[i\] = tensor;
        }
    
        // 设置输入张量的数据
        returnCode = SetInputData(inputTensors, inputCount);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "SetInputData failed.");
    
        // 执行推理
        returnCode = OH\_NNExecutor\_RunSync(executor, inputTensors, inputCount, outputTensors, outputCount);
        CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNExecutor\_RunSync failed.");
    
        // 打印输出张量的数据
        Print(outputTensors, outputCount);
    
        // 清理输入和输出张量以及张量描述
        for (size\_t i = 0; i < inputCount; ++i) {
            returnCode = OH\_NNTensor\_Destroy(&inputTensors\[i\]);
            CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNTensor\_Destroy failed.");
            returnCode = OH\_NNTensorDesc\_Destroy(&inputTensorDescs\[i\]);
            CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNTensorDesc\_Destroy failed.");
        }
        for (size\_t i = 0; i < outputCount; ++i) {
            returnCode = OH\_NNTensor\_Destroy(&outputTensors\[i\]);
            CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNTensor\_Destroy failed.");
            returnCode = OH\_NNTensorDesc\_Destroy(&outputTensorDescs\[i\]);
            CHECKNEQ(returnCode, OH\_NN\_SUCCESS, OH\_NN\_FAILED, "OH\_NNTensorDesc\_Destroy failed.");
        }
    
        return OH\_NN\_SUCCESS;
    }
    
9.  构建端到端模型构造-编译-执行流程。
    
    步骤4-步骤8实现了模型的模型构造、编译和执行流程，并封装成多个函数，便于模块化开发。以下示例代码将串联这些函数， 形成一个完整的Neural Network Runtime使用流程。
    
    int main(int argc, char\*\* argv)
    {
        OH\_NNModel\* model = nullptr;
        OH\_NNCompilation\* compilation = nullptr;
        OH\_NNExecutor\* executor = nullptr;
        std::vector<size\_t> availableDevices;
    
        // 模型构造
        OH\_NN\_ReturnCode ret = BuildModel(&model);
        if (ret != OH\_NN\_SUCCESS) {
            std::cout << "BuildModel failed." << std::endl;
            OH\_NNModel\_Destroy(&model);
            return -1;
        }
    
        // 获取可执行的设备
        GetAvailableDevices(availableDevices);
        if (availableDevices.empty()) {
            std::cout << "No available device." << std::endl;
            OH\_NNModel\_Destroy(&model);
            return -1;
        }
    
        // 模型编译
        ret = CreateCompilation(model, availableDevices, &compilation);
        if (ret != OH\_NN\_SUCCESS) {
            std::cout << "CreateCompilation failed." << std::endl;
            OH\_NNModel\_Destroy(&model);
            OH\_NNCompilation\_Destroy(&compilation);
            return -1;
        }
    
        // 销毁模型实例
        OH\_NNModel\_Destroy(&model);
    
        // 创建模型的推理执行器
        executor = CreateExecutor(compilation);
        if (executor == nullptr) {
            std::cout << "CreateExecutor failed, no executor is created." << std::endl;
            OH\_NNCompilation\_Destroy(&compilation);
            return -1;
        }
    
        // 销毁编译实例
        OH\_NNCompilation\_Destroy(&compilation);
    
        // 使用上一步创建的执行器，执行推理计算
        ret = Run(executor, availableDevices);
        if (ret != OH\_NN\_SUCCESS) {
            std::cout << "Run failed." << std::endl;
            OH\_NNExecutor\_Destroy(&executor);
            return -1;
        }
    
        // 销毁执行器实例
        OH\_NNExecutor\_Destroy(&executor);
    
        return 0;
    }
    

#### 调测验证

1.  准备应用样例的编译配置文件。
    
    新建一个 CMakeLists.txt 文件，为开发步骤中的应用样例文件 nnrt\_example.cpp 添加编译配置。以下提供简单的 CMakeLists.txt 示例：
    
    cmake\_minimum\_required(VERSION 3.16)
    project(nnrt\_example C CXX)
    
    add\_executable(nnrt\_example
        ./nnrt\_example.cpp
    )
    
    target\_link\_libraries(nnrt\_example
        neural\_network\_runtime
        neural\_network\_core
    )
    
2.  编译应用样例。
    
    执行以下命令，在当前目录下新建build/目录，在build/目录下编译 nnrt\_example.cpp，得到二进制文件 nnrt\_example。
    
    mkdir build && cd build
    cmake -DCMAKE\_TOOLCHAIN\_FILE={交叉编译工具链的路径}/build/cmake/ohos.toolchain.cmake -DOHOS\_ARCH=arm64-v8a -DOHOS\_PLATFORM=OHOS -DOHOS\_STL=c++\_static ..
    make
    
3.  执行以下代码，将样例推送到设备上执行。
    
    \# 将编译得到的 \`nnrt\_example\` 推送到设备上，执行样例。
    hdc\_std file send ./nnrt\_example /data/local/tmp/.
    
    # 给测试用例可执行文件加上权限。
    hdc\_std shell "chmod +x /data/local/tmp/nnrt\_example"
    
    # 执行测试用例
    hdc\_std shell "/data/local/tmp/nnrt\_example"
    
    如果样例执行正常，应该得到以下输出。
    
    Output index: 0, value is: 0.000000.
    Output index: 1, value is: 2.000000.
    Output index: 2, value is: 4.000000.
    Output index: 3, value is: 6.000000.
    Output index: 4, value is: 8.000000.
    Output index: 5, value is: 10.000000.
    Output index: 6, value is: 12.000000.
    Output index: 7, value is: 14.000000.
    Output index: 8, value is: 16.000000.
    Output index: 9, value is: 18.000000.
    Output index: 10, value is: 20.000000.
    Output index: 11, value is: 22.000000.
    
4.  检查模型缓存（可选）。
    
    如果在调测环境下，Neural Network Runtime对接的HDI服务支持模型缓存功能，执行完 nnrt\_example, 可以在 /data/local/tmp 目录下找到生成的缓存文件。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/71/v3/cubLxmA8TJawIhZYHfpVvQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073804Z&HW-CC-Expire=86400&HW-CC-Sign=666D00189ABD006BC8DB222AE16A2A15D6B7BAF7E50D63C606B274696C2E093B)
    
    模型的IR需要传递到硬件驱动层，由HDI服务将统一的IR图，编译成硬件专用的计算图，编译的过程非常耗时。Neural Network Runtime支持计算图缓存的特性，可以将HDI服务编译生成的计算图，缓存到设备存储中。当下一次在同一个加速芯片上编译同一个模型时，通过指定缓存的路径，Neural Network Runtime可以直接加载缓存文件中的计算图，减少编译消耗的时间。
    
    检查缓存目录下的缓存文件：
    
    ls /data/local/tmp
    
    以下为打印结果：
    
    \# 0.nncache 1.nncache 2.nncache cache\_info.nncache
    
    如果缓存不再使用，需要手动删除缓存，可以参考以下命令，删除缓存文件。
    
    rm /data/local/tmp/\*nncache