Skip to main content
Qualcomm AI Runtime(QAIRT)SDK 提供用于示例 应用开发的 C++ API。Qualcomm AI Engine Direct(QNN) 和 Qualcomm Neural Processing Engine SDK(SNPE)均提供示例。这些示例可帮助您开始应用 开发。以下说明介绍如何构建、运行和浏览 源代码。它们演示了使用 QNN 或 SNPE API 运行模型的工作流。

构建并运行 QNN 示例应用

qnn-sample-app 位于 ${QNN_SDK_ROOT}/examples/QNN/SampleApp, 其中 QNN_SDK_ROOT 指 QNN SDK 解压所在的路径。

设置 QAIRT SDK

要为 QNN 示例应用设置工具链,请完成以下步骤:
  1. 下载 Qualcomm AI Runtime SDK。
  2. 提取并解压 SDK。
  3. 安装 SDK。 按照 Build QIM SDK 安装 SDK,其中包含 所需的交叉编译工具链。
    • 这些库使用 GCC-11.2 编译。
    • 使用 SDK 安装路径设置 SDK_PATH 环境变量。后续步骤会使用该安装路径(/path/to/extracted/toolchain)进行编译。

构建 QNN 示例应用

完成以下步骤以构建 QNN 示例应用。
  1. 进入示例应用目录。
  2. 为 GCC 工具链设置环境变量。
  3. 构建应用。
    这会创建两个文件夹。
    • bin:在各平台对应的目录中包含 qnn-sample-app 二进制文件。
    • obj:包含构建和链接可执行文件时使用的所有目标文件。

在 Linux(基于 Yocto)上运行 QNN 示例应用

构建好的 qnn-sample-app 可执行文件可以使用任何 QNN 后端运行模型。对于基于 Yocto scarthgap 的设备,后端可用于 aarch64-oe-linux-gcc11.2。
  1. 将工件推送到目标设备。
    如果设备上尚不存在 /etc/apps/ 目录,请创建它。
  2. 在主机上,使用 AI Hub 导出模型。 例如,要导出 InceptionV3 QNN 模型,请运行以下命令:
    请为目标设备上使用的相同 SDK 版本生成上下文二进制文件。
  3. 将导出的 InceptionV3 QNN 模型推送到目标设备。 将模型保存到 export_assets/inception_v3-qnn_context_binary-w8a8-<CHIPSET>。以下示例使用 QCS6490 作为芯片组。
    出现输入密码的提示时,输入 oelinux123。
  4. 在主机上,生成用于推理的虚拟输入文件并将其传输到目标设备。 a. 在 Python 环境中运行以下命令。
    b. 将 input.raw 文件传输到目标设备:
  5. 从主机通过 SSH 登录到目标设备。
  6. 创建 input_list.txt。
  7. 运行应用。
    请根据所选模型更新模型名称和 input_list。
    要获取帮助信息,请运行:

命令行参数

必需参数
  • --model:QNN 网络模型的路径。与 --retrieve_context 互斥。
  • --retrieve_context:用于加载已保存上下文和执行图的 缓存二进制文件的路径。与 --model 互斥。
  • --backend:用于运行模型的 QNN 后端的路径。
  • --input_list:列出网络输入的文件的路径。对于多个 图,请提供以逗号分隔的输入文件列表。
可选参数
  • --debug:保存所有网络层的输出。
  • --output_dir:输出目录(默认:./output)。
  • --output_data_type:输出数据类型(float_only、native_only、float_and_native)。
  • --input_data_type:输入数据类型(float 或 native)。
  • --op_packages:以逗号分隔的算子包和接口提供程序列表。
  • --profiling_level:性能分析级别(basic 或 detailed)。
  • --save_context:将后端上下文和图元数据保存到二进制文件。
  • --num_inferences:要执行的推理次数。
  • --log_level:最大日志级别(error、warn、info、verbose)。
  • --system_library:用于在上下文加载期间使用反射 API 的 libQnnSystem.so 的路径。
  • --version:打印 QNN SDK 版本。
  • --help:显示帮助信息。

工作流和 API 用法

使用以下推荐模式,通过 QNN API 开发 C++ 应用。
  1. 加载先决共享库。
  2. 使用 QNN API。 a. 使用 QNN 接口获取函数指针。
    b. 设置日志记录。
    c. 初始化后端。
    d. 初始化性能分析。
    e. 创建设备。
    f. 注册算子包。
    g. 创建上下文。
    h. 准备图。
    i. 完成图。
    j. 将上下文保存到二进制文件。
    k. 从缓存的二进制文件加载上下文。
    l. 运行图。
    m. 释放上下文。
    n. 终止后端。

加载先决共享库

QNN SDK 提供了各种共享库用于访问后端, 应用必须根据需要加载它们才能运行网络。 可通过以下方式之一在 QNN 中创建网络。
  • 使用 QNN API 直接在您的应用中构建网络。
  • 使用 QNN 转换器生成 QNN 网络的共享库。
qnn-sample-app 使用共享库方式。该网络可以 使用 SDK 中提供的任一 QNN 转换器生成,并 使用 qnn-model-lib-generator 编译为共享库。
对于 Windows 用户,请在以下说明中将所有 .so 文件替换为 对应的 .dll 文件。有关更多详情,请参阅 平台差异。

加载后端

QNN SDK 中提供了包括 CPU、GPU、HTP 和 DSP 在内的各种后端的 共享库。每个实现了 QNN API 的后端 都会暴露所有必要的符号,可通过动态加载 机制进行访问。 假设有一个名为 libQnnSampleBackend.so 的示例后端共享库, 可以按如下方式动态加载它:
要以共享库形式加载模型,我们假设有一个名为 libQnnSampleModel.so 的示例模型共享库,可以按如下方式 动态加载它:
另外,如果要从缓存的二进制文件创建上下文并执行图, 应用可以利用 QnnSystem API 检索与上下文 关联的元数据。可以按如下方式加载 libQnnSystem.so 共享库来访问 QnnSystem API:

解析共享库中的符号

共享库成功加载后,我们可以继续 解析访问 QNN API 所需的所有符号。 以下代码片段展示了一个在共享库中解析符号的 模板:
以下代码片段展示了如何解析实际 QNN API 的示例:
在 qnn-sample-app 源代码中,所有必要的符号都会被解析并 存储在类型为 QnnFunctionPointers 的结构体中,如下所示:
上述结构体可以在 ${QNN_SDK_ROOT}/examples/QNN/SampleApp/SampleApp/src/SampleApp.hpp 中找到。 本教程的其余部分将假定存在一个名为 m_qnnFunctionPointers、类型为 QnnFunctionPointers 的变量, 其中包含有效的函数指针。

QNN API 的用法

本节演示 QNN API 在客户端应用中的用法。

使用 QNN 接口获取函数指针

可以使用 QNN 接口机制来设置指向后端中 QNN API 的函数 指针表,而不必手动逐个解析 每个 API 的符号,这使得符号解析变得简单。QNN 接口的使用方式如下:
可以使用 QNN 系统接口来解析与 QNN 系统 API 相关的所有符号,如下所示:

设置日志记录

可以在初始化后端之前、动态加载后端 共享库之后设置日志记录。 要初始化日志记录,必须定义一个类型为 QnnLog_Callback_t 的 回调。示例定义如下:
可以将上述回调与最大日志级别一起注册到后端。 以下示例代码以最大日志级别 QNN_LOG_LEVEL_INFO 进行初始化:

初始化后端

成功初始化日志记录后,可以按如下方式初始化 后端:

初始化性能分析

如果需要性能分析,在初始化后端之后,可以设置一个性能分析 句柄。此性能分析句柄可以在之后 用于任何支持性能分析的 API。 可以在后端以 basic 性能分析级别创建性能分析句柄, 如下所示:

创建设备

可以按如下方式创建设备:
按照 QNN HTP Backend API 中的定义设置 devConfig

注册算子包

算子包是向后端提供包含算子的库的一种方式。 可以按如下方式注册它们:

创建上下文

可以在后端中按如下方式创建上下文:

准备图

qnn-sample-app 依赖某个转换器的输出在后端中 创建 QNN 网络。composeGraphsFnHandle 映射到 模型共享库中的 QnnModel_composeGraphs API,该 API 以 qnn_wrapper_api::GraphInfo_t*** 作为参数之一。 函数 composeGraphsFnHandle 会对后端进行必要的调用 以创建网络。它还会将执行图所需的所有必要 信息(例如与图相关的输入和输出张量的信息)写入 结构体 graphsInfo,如以下代码块所示:
此时,上下文将包含 libQnnSampleModel.so 中存在的 所有图。

完成图

可以按如下方式完成上一步中添加的 图:

将上下文保存到二进制文件

当上下文中的所有图都完成后,用户应用 可以选择将上下文保存为二进制文件以供将来使用。保存 上下文的优点是,将来可以检索它 以执行其中包含的图,而无需再次完成 它们。这将大大节省网络执行期间的 初始化时间。 可以按如下方式保存上下文:

从缓存的二进制文件加载上下文

像上一步那样保存为二进制文件的上下文,可以 被加载,作为每次创建新上下文的替代方案。 以下代码片段演示了此步骤:

运行图

在创建了上下文、添加并完成了图之后, 或者在从二进制文件检索了上下文之后,可以执行上下文中的一个 或多个图。 运行图涉及以下操作:
  1. 设置输入和输出张量。
  2. 将输入数据填充到输入张量中。
  3. 调用后端中的执行方法。
  4. 获取输出并保存。
以下代码片段演示了这一过程:
IOTensor 是随源代码提供的实用工具,位于 ${QNN_SDK_ROOT}/examples/QNN/SampleApp/SampleApp/src/Utils/IOTensor.cpp。 它公开了一些有助于执行图的方法, 这些方法在前面的代码片段中已被使用:
  1. setupInputAndOutputTensors,用于设置与输入 和输出张量相关的结构。
  2. populateInputTensors,用于将输入数据复制到输入张量 结构中。
  3. tearDownInputAndOutputTensors,用于清理与 输入和输出张量关联的资源。
有关这些 API 的更多详情,请参阅 IOTensor 源代码。

释放上下文

所有执行完成后,可以按如下方式释放 上下文:

终止后端

可以按如下方式终止后端:

SNPE 示例应用

有关使用 SNPE 的 C++ API 和示例应用执行,请参阅 Qualcomm AI Runtime SDK 文档。