构建并运行 QNN 示例应用
qnn-sample-app 位于 ${QNN_SDK_ROOT}/examples/QNN/SampleApp,
其中 QNN_SDK_ROOT 指 QNN SDK 解压所在的路径。
设置 QAIRT SDK
要为 QNN 示例应用设置工具链,请完成以下步骤: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 导出模型
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++ 应用。1
步骤 1
2
步骤 2
使用 QNN API
1
步骤 2.1
2
步骤 2.2
3
步骤 2.3
4
步骤 2.4
5
步骤 2.5
6
步骤 2.6
7
步骤 2.7
8
步骤 2.8
9
步骤 2.9
10
步骤 2.10
11
步骤 2.11
12
步骤 2.12
13
步骤 2.13
14
步骤 2.14
加载先决共享库
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 的示例后端共享库, 可以按如下方式动态加载它:解析共享库中的符号
共享库成功加载后,我们可以继续 解析访问 QNN API 所需的所有符号。 以下代码片段展示了一个在共享库中解析符号的 模板:${QNN_SDK_ROOT}/examples/QNN/SampleApp/SampleApp/src/SampleApp.hpp 中找到。
本教程的其余部分将假定存在一个名为
m_qnnFunctionPointers、类型为 QnnFunctionPointers 的变量,
其中包含有效的函数指针。
QNN API 的用法
本节演示 QNN API 在客户端应用中的用法。使用 QNN 接口获取函数指针
可以使用 QNN 接口机制来设置指向后端中 QNN API 的函数 指针表,而不必手动逐个解析 每个 API 的符号,这使得符号解析变得简单。QNN 接口的使用方式如下:设置日志记录
可以在初始化后端之前、动态加载后端 共享库之后设置日志记录。 要初始化日志记录,必须定义一个类型为 QnnLog_Callback_t 的 回调。示例定义如下:初始化后端
成功初始化日志记录后,可以按如下方式初始化 后端:初始化性能分析
如果需要性能分析,在初始化后端之后,可以设置一个性能分析 句柄。此性能分析句柄可以在之后 用于任何支持性能分析的 API。 可以在后端以 basic 性能分析级别创建性能分析句柄, 如下所示:创建设备
可以按如下方式创建设备:注册算子包
算子包是向后端提供包含算子的库的一种方式。 可以按如下方式注册它们:创建上下文
可以在后端中按如下方式创建上下文:准备图
qnn-sample-app 依赖某个转换器的输出在后端中 创建 QNN 网络。composeGraphsFnHandle 映射到 模型共享库中的 QnnModel_composeGraphs API,该 API 以 qnn_wrapper_api::GraphInfo_t*** 作为参数之一。 函数 composeGraphsFnHandle 会对后端进行必要的调用 以创建网络。它还会将执行图所需的所有必要 信息(例如与图相关的输入和输出张量的信息)写入 结构体 graphsInfo,如以下代码块所示:完成图
可以按如下方式完成上一步中添加的 图:将上下文保存到二进制文件
当上下文中的所有图都完成后,用户应用 可以选择将上下文保存为二进制文件以供将来使用。保存 上下文的优点是,将来可以检索它 以执行其中包含的图,而无需再次完成 它们。这将大大节省网络执行期间的 初始化时间。 可以按如下方式保存上下文:从缓存的二进制文件加载上下文
像上一步那样保存为二进制文件的上下文,可以 被加载,作为每次创建新上下文的替代方案。 以下代码片段演示了此步骤:运行图
在创建了上下文、添加并完成了图之后, 或者在从二进制文件检索了上下文之后,可以执行上下文中的一个 或多个图。 运行图涉及以下操作:1
设置输入和输出张量
2
将输入数据填充到输入张量中
3
调用后端中的执行方法
4
获取输出并保存
${QNN_SDK_ROOT}/examples/QNN/SampleApp/SampleApp/src/Utils/IOTensor.cpp。
它公开了一些有助于执行图的方法,
这些方法在前面的代码片段中已被使用:
- setupInputAndOutputTensors,用于设置与输入 和输出张量相关的结构。
- populateInputTensors,用于将输入数据复制到输入张量 结构中。
- tearDownInputAndOutputTensors,用于清理与 输入和输出张量关联的资源。

