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

单击此处下载 Qualcomm AI Runtime SDK。
如果您想下载特定的 QAIRT 版本,请遵循此处的说明。
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:包含构建和链接可执行文件时使用的所有目标文件。

在 Ubuntu 上运行 QNN 示例应用

构建好的 qnn-sample-app 可执行文件可以使用任何 QNN 后端运行模型。对于基于 Yocto scarthgap 的设备,后端可用于 aarch64-oe-linux-gcc11.2。
1

将工件推送到目标设备

如果设备上尚不存在 /etc/apps/ 目录,请创建它。
2

在主机上,使用 AI Hub 导出模型

使用 AI Hub 导出模型。例如,要导出 InceptionV3 QNN 模型,请运行以下命令:
请为目标设备上使用的相同 SDK 版本生成上下文二进制文件。
3

将导出的 InceptionV3 QNN 模型推送到目标设备

将模型保存到 export_assets/inception_v3-qnn_context_binary-w8a8-<CHIPSET>。以下示例使用 QCS6490 作为芯片组。
4

在主机上,生成用于推理的虚拟输入文件并将其传输到目标设备

1

在 Python 环境中运行以下命令

2

将 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++ 应用。

加载先决共享库

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 文档。