|
1 | 1 | # Setting up QNN Backend |
2 | 2 |
|
3 | | -This is a tutorial for building and running Qualcomm AI Engine Direct backend, |
| 3 | +Please refer to [Building and Running ExecuTorch with Qualcomm AI Engine Direct Backend](../../docs/source/build-run-qualcomm-ai-engine-direct-backend.md). |
| 4 | + |
| 5 | +That is a tutorial for building and running Qualcomm AI Engine Direct backend, |
4 | 6 | including compiling a model on a x64 host and running the inference |
5 | 7 | on a Android device. |
6 | | - |
7 | | - |
8 | | -## Prerequisite |
9 | | - |
10 | | -Please finish tutorial [Setting up executorch](../../docs/source/getting-started-setup.md). |
11 | | - |
12 | | - |
13 | | -## Conventions |
14 | | - |
15 | | -`$QNN_SDK_ROOT` refers to the root of Qualcomm AI Engine Direct SDK, |
16 | | -i.e., the directory containing `QNN_README.txt`. |
17 | | - |
18 | | -`$ANDROID_NDK_ROOT` refers to the root of Android NDK. |
19 | | - |
20 | | -`$EXECUTORCH_ROOT` refers to the root of executorch git repository. |
21 | | - |
22 | | - |
23 | | -## Environment Setup |
24 | | - |
25 | | -### Download Qualcomm AI Engine Direct SDK |
26 | | - |
27 | | -Navigate to [Qualcomm AI Engine Direct SDK](https://developer.qualcomm.com/software/qualcomm-ai-engine-direct-sdk) and follow the download button. |
28 | | - |
29 | | -You might need to apply for a Qualcomm account to download the SDK. |
30 | | - |
31 | | -After logging in, search Qualcomm AI Stack at the *Tool* panel. |
32 | | -You can find Qualcomm AI Engine Direct SDK under the AI Stack group. |
33 | | - |
34 | | -Please download the Linux version, and follow instructions on the page to |
35 | | -extract the file. |
36 | | - |
37 | | -The SDK should be installed to somewhere `/opt/qcom/aistack/qnn` by default. |
38 | | - |
39 | | -### Download Android NDK |
40 | | - |
41 | | -Please navigate to [Android NDK](https://developer.android.com/ndk) and download |
42 | | -a version of NDK. We recommend LTS version, currently r25c. |
43 | | - |
44 | | -### Setup environment variables |
45 | | - |
46 | | -We need to make sure Qualcomm AI Engine Direct libraries can be found by |
47 | | -the dynamic linker on x64. Hence we set `LD_LIBRARY_PATH`. In production, |
48 | | -we recommend users to put libraries in default search path or use `rpath` |
49 | | -to indicate the location of libraries. |
50 | | - |
51 | | -Further, we set up `$PYTHONPATH` because it's easier to develop and import executorch Python APIs. Users might also build and install executorch package as usual python package. |
52 | | - |
53 | | -```bash |
54 | | -export LD_LIBRARY_PATH=$QNN_SDK_ROOT/lib/x86_64-linux-clang/:$LD_LIBRARY_PATH |
55 | | -export PYTHONPATH=$EXECUTORCH_ROOT/.. |
56 | | -``` |
57 | | - |
58 | | -Note: Since we set `PYTHONPATH`, we may have issue with finding `program.fbs` |
59 | | -and `scalar_type.fbs` when we export a model, because they are installed into |
60 | | -`pip-out` directory with the same package name pattern. A workaround is that |
61 | | -we copy `$EXECUTORCH_ROOT/pip-out/lib.linux-x86_64-cpython-310/executorch/exir/_serialize/program.fbs` |
62 | | -and `$EXECUTORCH_ROOT/pip-out/lib.linux-x86_64-cpython-310/executorch/exir/_serialize/scalar_type.fbs` |
63 | | -to `$EXECUTORCH_ROOT/exir/_serialize/`. |
64 | | - |
65 | | - |
66 | | -## End to End Inference |
67 | | - |
68 | | -### Step 1: Build Python APIs for AOT compilation on x64 |
69 | | - |
70 | | -Python APIs on x64 are required to compile models to Qualcomm AI Engine Direct binary. |
71 | | -Make sure `buck2` is under a directory in `PATH`. |
72 | | - |
73 | | -```bash |
74 | | -cd $EXECUTORCH_ROOT |
75 | | -mkdir build_x86_64 |
76 | | -cd build_x86_64 |
77 | | -cmake .. -DEXECUTORCH_BUILD_QNN=ON -DQNN_SDK_ROOT=${QNN_SDK_ROOT} |
78 | | -cmake --build . -t "PyQnnManagerAdaptor" "PyQnnWrapperAdaptor" -j8 |
79 | | - |
80 | | -# install Python APIs to correct import path |
81 | | -# The filename might vary depending on your Python and host version. |
82 | | -cp -f backends/qualcomm/PyQnnManagerAdaptor.cpython-310-x86_64-linux-gnu.so $EXECUTORCH_ROOT/backends/qualcomm/python |
83 | | -cp -f backends/qualcomm/PyQnnWrapperAdaptor.cpython-310-x86_64-linux-gnu.so $EXECUTORCH_ROOT/backends/qualcomm/python |
84 | | -``` |
85 | | - |
86 | | - |
87 | | -### Step 2: Build `qnn_executor_runner` for Android |
88 | | - |
89 | | -`qnn_executor_runner` is an executable running the compiled model. |
90 | | - |
91 | | -You might want to ensure the correct `flatc`. `flatc` can be built along with the above step. For example, we can find `flatc` in `build_x86_64/third-party/flatbuffers/`. |
92 | | - |
93 | | -We can prepend `$EXECUTORCH_ROOT/build_x86_64/third-party/flatbuffers` to `PATH`. Then below cross-compiling can find the correct flatbuffer compiler. |
94 | | - |
95 | | -Commands to build `qnn_executor_runner` for Android: |
96 | | - |
97 | | -```bash |
98 | | -cd $EXECUTORCH_ROOT |
99 | | -mkdir build_android |
100 | | -cd build_android |
101 | | -# build executorch & qnn_executorch_backend |
102 | | -cmake .. \ |
103 | | - -DCMAKE_INSTALL_PREFIX=$PWD \ |
104 | | - -DEXECUTORCH_BUILD_QNN=ON \ |
105 | | - -DEXECUTORCH_BUILD_SDK=ON \ |
106 | | - -DEXECUTORCH_ENABLE_EVENT_TRACER=ON \ |
107 | | - -DQNN_SDK_ROOT=$QNN_SDK_ROOT \ |
108 | | - -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \ |
109 | | - -DANDROID_ABI='arm64-v8a' \ |
110 | | - -DANDROID_NATIVE_API_LEVEL=23 \ |
111 | | - -B$PWD |
112 | | - |
113 | | -cmake --build $PWD -j16 --target install |
114 | | - |
115 | | -cmake ../examples/qualcomm \ |
116 | | - -DCMAKE_TOOLCHAIN_FILE=$ANDROID_NDK_ROOT/build/cmake/android.toolchain.cmake \ |
117 | | - -DANDROID_ABI='arm64-v8a' \ |
118 | | - -DANDROID_NATIVE_API_LEVEL=23 \ |
119 | | - -DCMAKE_PREFIX_PATH="$PWD/lib/cmake/ExecuTorch;$PWD/third-party/gflags;" \ |
120 | | - -DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH \ |
121 | | - -Bexamples/qualcomm |
122 | | - |
123 | | -cmake --build examples/qualcomm -j16 |
124 | | -``` |
125 | | -**Note:** If you want to build for release, add `-DCMAKE_BUILD_TYPE=Release` to the `cmake` command options. |
126 | | - |
127 | | -You can find `qnn_executor_runner` under `build_android/examples/qualcomm/`. |
128 | | - |
129 | | - |
130 | | -### Step 3: Compile a model |
131 | | - |
132 | | -``` |
133 | | -python -m examples.qualcomm.scripts.export_example --model_name mv2 |
134 | | -``` |
135 | | - |
136 | | -Then the generated `mv2.pte` can be run on the device by |
137 | | -`build_android/backends/qualcomm/qnn_executor_runner` with Qualcomm AI Engine |
138 | | -Direct backend. |
139 | | - |
140 | | -[**Note**] To get proper accuracy, please apply calibrations with representative |
141 | | -dataset, which could be learnt more from examples under `examples/qualcomm/`. |
142 | | - |
143 | | - |
144 | | -### Step 4: Model Inference |
145 | | - |
146 | | -The backend rely on Qualcomm AI Engine Direct SDK libraries. |
147 | | - |
148 | | -You might want to follow docs in Qualcomm AI Engine Direct SDK to setup the device environment. |
149 | | -Or see below for a quick setup for testing: |
150 | | - |
151 | | -```bash |
152 | | -# make sure you have write-permission on below path. |
153 | | -DEVICE_DIR=/data/local/tmp/executorch_test/ |
154 | | -adb shell "mkdir -p ${DEVICE_DIR}" |
155 | | -adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtp.so ${DEVICE_DIR} |
156 | | -adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpV69Stub.so ${DEVICE_DIR} |
157 | | -adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpV73Stub.so ${DEVICE_DIR} |
158 | | -adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnHtpV75Stub.so ${DEVICE_DIR} |
159 | | -adb push ${QNN_SDK_ROOT}/lib/aarch64-android/libQnnSystem.so ${DEVICE_DIR} |
160 | | -adb push ${QNN_SDK_ROOT}/lib/hexagon-v69/unsigned/libQnnHtpV69Skel.so ${DEVICE_DIR} |
161 | | -adb push ${QNN_SDK_ROOT}/lib/hexagon-v73/unsigned/libQnnHtpV73Skel.so ${DEVICE_DIR} |
162 | | -adb push ${QNN_SDK_ROOT}/lib/hexagon-v75/unsigned/libQnnHtpV75Skel.so ${DEVICE_DIR} |
163 | | -``` |
164 | | - |
165 | | -We also need to indicate dynamic linkers on Android and Hexagon where to find these libraries |
166 | | -by setting `ADSP_LIBRARY_PATH` and `LD_LIBRARY_PATH`. |
167 | | - |
168 | | -So, we can run `qnn_executor_runner` like |
169 | | -```bash |
170 | | -adb push mv2.pte ${DEVICE_DIR} |
171 | | -adb push ${EXECUTORCH_ROOT}/build_android/examples/qualcomm/qnn_executor_runner ${DEVICE_DIR} |
172 | | -adb shell "cd ${DEVICE_DIR} \ |
173 | | - && export LD_LIBRARY_PATH=${DEVICE_DIR} \ |
174 | | - && export ADSP_LIBRARY_PATH=${DEVICE_DIR} \ |
175 | | - && ./qnn_executor_runner --model_path ./mv2_qnn.pte" |
176 | | -``` |
177 | | - |
178 | | -You should see the following result. |
179 | | -Note that no output file will be generated in this example. |
180 | | -``` |
181 | | -I 00:00:00.133366 executorch:qnn_executor_runner.cpp:156] Method loaded. |
182 | | -I 00:00:00.133590 executorch:util.h:104] input already initialized, refilling. |
183 | | -I 00:00:00.135162 executorch:qnn_executor_runner.cpp:161] Inputs prepared. |
184 | | -I 00:00:00.136768 executorch:qnn_executor_runner.cpp:278] Model executed successfully. |
185 | | -[INFO][Qnn ExecuTorch] Destroy Qnn backend parameters |
186 | | -[INFO][Qnn ExecuTorch] Destroy Qnn context |
187 | | -[INFO][Qnn ExecuTorch] Destroy Qnn device |
188 | | -[INFO][Qnn ExecuTorch] Destroy Qnn backend |
189 | | -``` |
0 commit comments