|
6 | 6 | - NNAPI |
7 | 7 | - HIAI |
8 | 8 |
|
9 | | -目前NPU相关后端均不支持可变形状、控制流等动态模型,算子数相比CPU/GPU支持要少,建议根据NPU是否能跑通,反复调整模型结构。 |
| 9 | +## QNN |
10 | 10 |
|
11 | | -同时,由于QNN、CoreML与NNAPI在MNN中共用同一个Backend Type,这三个后端对应的编译宏MNN_QNN、MNN_COREML、MNN_NNAPI在编译时,至多只能打开一个。 |
| 11 | +### QNN后端整体介绍 |
12 | 12 |
|
13 | | -## QNN |
14 | | -适用于使用高通芯片且配备高通Hexagon张量处理器(Hexagon Tensor Processor,HTP)的机型,可参考[高通官网的设备支持列表](https://docs.qualcomm.com/bundle/publicresource/topics/80-63442-50/overview.html#supported-snapdragon-devices)。 |
| 13 | +- MNN通过调用QNN SDK的CPP API构建了MNN-QNN后端,以期在能够使用高通NPU的设备上取得推理加速。 |
| 14 | +- 我们支持了两种运行模式: |
| 15 | + - 在线构图模式,在线编译和序列化QNN计算图。 |
| 16 | + - 支持静态形状的常规模型的推理。 |
| 17 | + - 离线构图模式则先借助MNN的离线工具缓存QNN计算图的序列化产物,接着在运行时直接读取产物,可以节省初始化时间。 |
| 18 | + - 支持静态形状/有限形状组合的常规模型的推理。 |
| 19 | + - 可支持部分llm模型的推理加速。 |
| 20 | + |
| 21 | +### 准备工作 |
| 22 | + |
| 23 | +#### 开发环境 |
| 24 | +- Host |
| 25 | + - 在线构图模式:无要求。 |
| 26 | + - 离线构图模式:一台x86_64,Linux的机器(链路中的部分QNN工具必须在此环境中运行)。 |
| 27 | +- Device |
| 28 | + - 一台可以使用高通NPU的设备;为便于陈述,下文假设这是一台Android系统的设备。 |
| 29 | + |
| 30 | +#### 明确硬件架构 |
| 31 | + |
| 32 | +QNN后端的部分使用步骤(如生成离线产物,确定QNN的NPU库依赖等)需要指定device的硬件架构对应的SOC ID以及HEXAGON ARCH。对于一些常见的硬件架构,我们列举如下供你参考: |
| 33 | + |
| 34 | +| 硬件 | SOC ID | HEXAGON ARCH | |
| 35 | +| :------ | :----- | :----------- | |
| 36 | +| 8 Gen 1 | 36 | 69 | |
| 37 | +| 8 Gen 2 | 43 | 73 | |
| 38 | +| 8 Gen 3 | 57 | 75 | |
| 39 | +| 8 Elite | 69 | 79 | |
| 40 | + |
| 41 | +对于其他的硬件架构,你可以参考高通官网的设备支持列表。 |
| 42 | + |
| 43 | +#### 获得QNN依赖 |
15 | 44 |
|
16 | | -### 获得QNN依赖 |
17 | | -QNN后端依赖QNN SDK中的`/include/QNN`与`lib`,首先,我们需要获得相关依赖。 |
| 45 | +MNN-QNN后端依赖QNN SDK中的`include/QNN`与`lib`,可通过以下步骤获取依赖: |
18 | 46 | - [注册高通账号](https://myaccount.qualcomm.com/signup) |
19 | | -- 访问Qualcomm AI Engine Direct SDK(即QNN SDK)[官网](https://www.qualcomm.com/developer/software/qualcomm-ai-engine-direct-sdk),下载SDK。 |
20 | | -- 参考以下指令,将下载的sdk中的`/include/QNN`与`lib`拷贝到MNN源码中的对应位置。 |
| 47 | +- 访问Qualcomm AI Engine Direct SDK(即QNN SDK),下载SDK,并解压。比如`/home/xiaying/third/qnn/qairt/2.38.0.250901` |
| 48 | +- 修改`~/.bashrc` ,增加SDK路径到环境变量, 然后运行 `source ~/.bashrc` 或者重启终端。eg: |
| 49 | + |
21 | 50 | ``` |
22 | | -QNN_SDK_ROOT="/YOUR/QNN/SDK/PATH" # modify this variable according to your environment |
23 | | -MNN_ROOT="/YOUR/MNN/PATH" # modify this variable according to your environment |
24 | | -INCLUDE_SRC="${QNN_SDK_ROOT}/include/QNN" |
25 | | -LIB_SRC="${QNN_SDK_ROOT}/lib" |
26 | | -INCLUDE_DEST="${MNN_ROOT}/source/backend/qnn/3rdParty/include" |
27 | | -LIB_DEST="${MNN_ROOT}/source/backend/qnn/3rdParty/lib" |
28 | | -mkdir "${MNN_ROOT}/source/backend/qnn/3rdParty" |
29 | | -cp -r ${INCLUDE_SRC} ${INCLUDE_DEST} |
30 | | -cp -r ${LIB_SRC} ${LIB_DEST} |
| 51 | +export QNN_SDK_ROOT=/home/xiaying/third/qnn/qairt/2.38.0.250901 |
| 52 | +export QNN_ROOT=/home/xiaying/third/qnn/qairt/2.38.0.250901 |
| 53 | +export HEXAGON_SDK_ROOT=/home/xiaying/third/qnn/qairt/2.38.0.250901 |
31 | 54 | ``` |
32 | 55 |
|
33 | | -### QNN后端编译 |
34 | | -- 编译 MNN 时打开编译宏`MNN_QNN`,即`-DMNN_QNN=ON`。 |
35 | | -- 如果运行离线编译QNN模型(离线编译方法:使用MNN2QNNModel工具),需要开启`MNN_WITH_PLUGIN`宏。若需要减小库体积,可以选择关闭`MNN_QNN_ONLINE_FINALIZE`宏 |
| 56 | +### 在线构图模式,推理常规模型 |
| 57 | +在线构图模式的使用步骤与其他后端基本一致,主要包含以下三部分。 |
| 58 | + |
| 59 | +#### Host,交叉编译Device侧的MNN库及AI应用程序 |
| 60 | +- 参考[“主库编译”](../compile/engine.md#主库编译),配置Android系统的编译环境及CMake变量。 |
| 61 | +- 添加额外的CMake变量并编译:`-DMNN_QNN=ON`、`-DMNN_QNN_CONVERT_MODE=OFF`、`-DMNN_WITH_PLUGIN=OFF`。 |
36 | 62 |
|
| 63 | +#### 推送资源至Device |
37 | 64 |
|
38 | | -### QNN后端运行 |
39 | | -- Backend Type设置为`MNN_FORWARD_NN`,即 5 。 |
40 | | -- 除MNN相关的库之外,QNN后端在运行时还依赖四个QNN库,可参考以下指令,将QNN中的库拷贝到设备中。其中变量`HEXAGON_ARCH`需要与你的目标机型匹配,可参考[高通官网的设备支持列表](https://docs.qualcomm.com/bundle/publicresource/topics/80-63442-50/overview.html#supported-snapdragon-devices),如8gen3的设备,需要设定`HEXAGON_ARCH="75"`。 |
| 65 | +参考下面的指令,将以下资源推送到Device侧 |
| 66 | +- AI应用程序。 |
| 67 | +- 交叉编译得到的Device侧的MNN库。 |
| 68 | +- QNN库(`libQnnHtp.so`、`libQnnHtpV${HEXAGON_ARCH}Stub.so`、`libQnnHtpV${HEXAGON_ARCH}Skel.so`、`libQnnHtpPrepare.so`)。 |
| 69 | +- MNN模型。 |
41 | 70 | ``` |
42 | 71 | HEXAGON_ARCH="75" # modify this variable according to your environment |
43 | | -MNN_ROOT="/YOUR/MNN/PATH" # modify this variable according to your environment |
44 | | -ANDROID_PATH="/data/local/tmp" |
45 | | -adb push ${MNN_ROOT}/source/backend/qnn/3rdParty/lib/aarch64-android/libQnnHtp.so ${ANDROID_PATH}/libQnnHtp.so |
46 | | -
|
47 | | -/* |
48 | | -如下libQnnHtpPrepare.so和libQnnSystem.so两个库,根据情况二选一 |
49 | | -- 如果在线生成qnn图模型,运行时需要libQnnHtpPrepare.so |
50 | | -- 如果离线生成qnn图模型,运行时需要libQnnSystem.so |
51 | | -*/ |
52 | | -adb push ${MNN_ROOT}/source/backend/qnn/3rdParty/lib/aarch64-android/libQnnHtpPrepare.so ${ANDROID_PATH}/libQnnHtpPrepare.so |
53 | | -adb push ${MNN_ROOT}/source/backend/qnn/3rdParty/lib/aarch64-android/libQnnSystem.so ${ANDROID_PATH}/libQnnSystem.so |
54 | | -
|
55 | | -adb push ${MNN_ROOT}/source/backend/qnn/3rdParty/lib/aarch64-android/libQnnHtpV${HEXAGON_ARCH}Stub.so ${ANDROID_PATH}/libQnnHtpV${HEXAGON_ARCH}Stub.so |
56 | | -adb push ${MNN_ROOT}/source/backend/qnn/3rdParty/lib/hexagon-v${HEXAGON_ARCH}/unsigned/libQnnHtpV${HEXAGON_ARCH}Skel.so ${ANDROID_PATH}/libQnnHtpV${HEXAGON_ARCH}Skel.so |
| 72 | +MNN_ROOT_PATH="/YOUR/MNN/ROOT/PATH" # modify this variable according to your environment |
| 73 | +BUILD_ANDROID_PATH="/your/build/andorid/path" # modify this variable according to your environment |
| 74 | +ANDROID_WORKING_DIR="/data/local/tmp" # modify this variable according to your environment |
| 75 | +
|
| 76 | +# push mnn libs |
| 77 | +cd ${BUILD_ANDROID_PATH} |
| 78 | +find . -name "*.so" | while read solib; do |
| 79 | + adb push $solib ${ANDROID_WORKING_DIR} |
| 80 | +done |
| 81 | +cd - |
| 82 | +
|
| 83 | +# push your AI exe |
| 84 | +adb push /your/AI/exe ${ANDROID_WORKING_DIR} |
| 85 | +
|
| 86 | +# push QNN libs |
| 87 | +adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtp.so ${ANDROID_WORKING_DIR} |
| 88 | +adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpV${HEXAGON_ARCH}Stub.so ${ANDROID_WORKING_DIR} |
| 89 | +adb push ${QNN_SDK_ROOT}/lib/hexagon-v${HEXAGON_ARCH}/unsigned/libQnnHtpV${HEXAGON_ARCH}Skel.so ${ANDROID_WORKING_DIR} |
| 90 | +# The following lib is only needed in the online case. |
| 91 | +adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpPrepare.so ${ANDROID_WORKING_DIR} |
| 92 | +
|
| 93 | +# push MNN models |
| 94 | +adb push model.mnn ${ANDROID_WORKING_DIR} |
57 | 95 | ``` |
58 | | -- 为了动态链接到QNN HTP相关的库,需要在环境变量`ADSP_LIBRARY_PATH`中添加QNN HTP库所在的目录(部分机型上有效)。如果这样也没法成功链接,可将可执行文件push到QNN HTP库所在目录(如`/data/local/tmp`),cd到对应目录后,再运行可执行文件,参考如下指令。 |
| 96 | + |
| 97 | +#### Device,链接并运行 |
| 98 | +- 链接QNN库 |
| 99 | + - 为了动态链接到QNN HTP相关的库,需要在环境变量`ADSP_LIBRARY_PATH`中添加QNN HTP库所在的目录(部分机型上有效)。如果这样也没法成功链接,可将可执行文件,QNN HTP库推送至同一目录,cd到对应目录后,再运行可执行文件,参考如下指令。 |
59 | 100 | ``` |
60 | | -adb shell "cd /data/local/tmp && LD_LIBRARY_PATH=/data/local/tmp ADSP_LIBRARY_PATH=/data/local/tmp ./MyExe.out" |
| 101 | +adb shell "cd ${ANDROID_WORKING_DIR} && export LD_LIBRARY_PATH=.:${ANDROID_LD_LIBRARY_PATH} && export ADSP_LIBRARY_PATH=.:${ANDROID_ADSP_LIBRARY_PATH} && ./your/mnn/qnn/ai/exe" |
61 | 102 | ``` |
| 103 | +- 配置MNN |
| 104 | + - Backend Type设置为`MNN_FORWARD_NN`,即5。 |
| 105 | + - 在使用Module API推理时,需要设定`Module::Config`中的`shapeMutable`字段为`false`。 |
| 106 | + |
| 107 | +### 离线构图模式,推理常规模型 |
| 108 | +相较于在线构图模式,离线构图模式额外包含一次编译(构建生成离线产物需要的MNN库)以及一个模型转换步骤(将原始的MNN模型转化成QNN产物),具体如下。 |
| 109 | + |
| 110 | +#### Host,编译生成离线模式产物需要的的MNN库及相应MNN离线工具 |
| 111 | +- 添加额外的CMake变量并编译:`-DMNN_QNN=ON`、`-DMNN_QNN_CONVERT_MODE=ON`、`-DMNN_WITH_PLUGIN=OFF`、`-DMNN_BUILD_TOOLS=ON`。 |
| 112 | + |
| 113 | +#### Host,生成QNN离线构图产物 |
| 114 | +调用`MNN2QNNModel`工具,针对Device的硬件架构,生成QNN离线产物(`model_${SOC_ID}_${HEXAGON_ARCH}.bin`)以及替代模型(`model_${SOC_ID}_${HEXAGON_ARCH}.mnn`),具体可参考[该工具的用法](../tools/convert.md#mnn2qnnmodel)。 |
| 115 | + |
| 116 | +#### Host,交叉编译Device侧的MNN库及AI应用程序 |
| 117 | +- 参考[“主库编译”](../compile/engine.md#主库编译),配置Android系统的编译环境及CMake变量。 |
| 118 | +- 添加额外的CMake变量并编译:`-DMNN_QNN=ON`、`-DMNN_QNN_CONVERT_MODE=OFF`、`-DMNN_WITH_PLUGIN=ON`。 |
| 119 | + |
| 120 | +#### 推送资源至Device |
| 121 | +与[在线构图模式的情况](#推送资源至device)类似,但有以下两点不同: |
| 122 | +- 依赖的QNN库变为`libQnnHtp.so`、`libQnnHtpV${HEXAGON_ARCH}Stub.so`、`libQnnHtpV${HEXAGON_ARCH}Skel.so`、`libQnnSystem.so`(不再依赖`libQnnHtpPrepare.so`,而是依赖`libQnnSystem.so`)。 |
| 123 | +- 不再使用原始的MNN模型,而是需要QNN离线产物(`model_${SOC_ID}_${HEXAGON_ARCH}.bin`)以及替代模型(`model_${SOC_ID}_${HEXAGON_ARCH}.mnn`)。 |
| 124 | + |
| 125 | +#### Device,链接并运行 |
| 126 | +- 配置MNN |
| 127 | + - 指定backend type为0(CPU)。读取并推理QNN离线产物的功能被封装在Plugin算子内,该算子被注册在CPU后端,因此,此时需要指定backend type为CPU。 |
| 128 | + - 在Device侧,如果你的离线产物和你的应用的工作目录不一致,那么你需要在程序中通过`Executor::RuntimeManager::setExternalPath`接口设定离线产物所在的目录。 |
| 129 | +- 链接QNN库 |
| 130 | + - 离线构图模式对于链接的要求和在线构图模式一致。 |
62 | 131 |
|
63 | | -### QNN量化功能说明 |
64 | | -- 仅权重量化(激活是浮点):只支持Linear权重int8、channel-wise的对称量化。 |
65 | | -- 激活&权重都量化:支持激活per-tensor对称量化,权重是int8/int4、channel-wise的对称量化。 |
66 | 132 |
|
67 | 133 | ## CoreML |
68 | 134 | 适用于 Mac / iOS / iPad |
|
0 commit comments