Skip to content

Experiment with Qualcomm (QNN) backend export #1333

Description

@msluszniak

Goal

Experiment with the ExecuTorch Qualcomm (QNN) backend: export a few easy
models and get them running correctly on-device on Qualcomm hardware (HTP /
Hexagon). Start with MobileNetV2 (small image classifier, quantizes cleanly
to int8 on HTP) as the proof-of-concept, then generalize.

Export scripts land in the GitLab export-scripts repo (universal-exporter
harness); RNE runtime integration is a separate follow-up (see below).

Key constraint: AOT compile needs x86_64 Linux (not macOS)

The QNN ahead-of-time (AOT) compiler is only shipped/verified for Ubuntu
22.04 x86_64
(or WSL). The QNN SDK ships no macOS host library — its host
libs are x86_64-linux-clang / x86_64-windows-msvc only. So the export flow
cannot run on an Apple Silicon Mac; only the device runtime libs are
cross-platform. Everything below runs on a Linux x86_64 box; only the phone is
shared.

Setup required

  • x86_64 Linux host — Ubuntu 22.04 (or WSL on Windows), g++ ≥ 13.

  • QNN SDK 2.37.0 (Qualcomm AI Engine Direct) from the Qualcomm developer
    portal, unpacked to e.g. ~/qnn-sdk/qairt/2.37.0.250724. 2.37 is the
    version ExecuTorch currently recommends for stability.

  • Android NDK (for building the on-device qnn_executor_runner).

  • ExecuTorch built from source with the Qualcomm backend enabled — the
    pip executorch wheel does not ship the QNN AOT libraries.
    bash git clone https://github.com/software-mansion-labs/executorch.git cd executorch && git submodule sync && git submodule update --init export QNN_SDK_ROOT=$HOME/qnn-sdk/qairt/2.37.0.250724 export ANDROID_NDK_ROOT=$HOME/Android/Sdk/ndk/<version> ./install_executorch.sh ./backends/qualcomm/scripts/build.sh # QNN AOT libs + android runtime

  • Env vars in every export shell:
    bash export QNN_SDK_ROOT=$HOME/qnn-sdk/qairt/2.37.0.250724 export EXECUTORCH_ROOT=$HOME/executorch source $QNN_SDK_ROOT/bin/envsetup.sh # sets LD_LIBRARY_PATH for QNN export PYTHONPATH=$EXECUTORCH_ROOT/..:$PYTHONPATH

  • A Qualcomm SoC device connected over adb. Read the chip and map it
    to a QcomChipset member (needed for --soc):
    bash adb shell getprop ro.soc.model # e.g. SM8650
    | ro.soc.model | QcomChipset | Marketing | Hexagon |
    |----------------|---------------|----------------------|---------|
    | SM8450 | SM8450 | Snapdragon 8 Gen 1 | v69 |
    | SM8550 | SM8550 | Snapdragon 8 Gen 2 | v73 |
    | SM8650 | SM8650 | Snapdragon 8 Gen 3 | v75 |
    | SM8750 | SM8750 | Snapdragon 8 Elite | v79 |

    Full list: `backends/qualcomm/serialization/qc_schema.py` (`QcomChipset`).
    

Export-scripts (universal-exporter) integration

Add QNN to the @nk/universal-exporter harness. QNN is a new backend axis — no
qnn/qualcomm reference exists there yet (inspect_pte.py's KNOWN_BACKENDS
already lists QnnBackend/HtpBackend, but there is no export path).

  • Register the backend — add "qnn" to _BACKENDS in
    src/exporter/spec.py (the harness hard-rejects unknown backends).
  • New model exporter_models/mobilenet_v2/:
    • model.jsonbackends includes "qnn".
    • qnn/config.json — local spec (no HF repo ships a QNN config). Variants:
      int8 (8a8w PTQ, quantized: true) and fp16. forward:
      input [1,3,224,224] float32 → output [1,1000] float32.
    • qnn/export.py — implements export(config, out_dir); lowers inline via
      the QNN HTP path (make_quantizer 8a8w → prepare_pt2e → calibrate →
      convert_pt2eto_edge_transform_and_lower_to_qnn with an HTP
      compiler spec for the target SoC). ExecuTorch imports stay inside
      export() per the harness contract. Calibrate on a small ImageNet-mini
      split for usable int8 accuracy.
  • skills/backends/qnn/SKILL.md — document the Linux-x86_64 + QNN-SDK
    requirement and the lowering recipe, mirroring the existing backend skills.

On-device validation

  • Build qnn_executor_runner for android (done by build.sh), push the
    .pte + inputs, run on the HTP, and sanity-check top-1/top-5. Fastest path
    is ExecuTorch's upstream one-shot example:
    bash python -m examples.qualcomm.scripts.mobilenet_v2 \ -b build-android -m SM8650 -s <serial> --dataset ./imagenet-mini/val # expect roughly top_1 ~70%, top_5 ~90%
    A healthy top-k proves the QNN compile + Hexagon execution path works on
    the target SoC.

Follow-up: react-native-executorch runtime integration (rne-rewrite)

Running a QNN-delegated .pte inside the RNE app is a separate, larger task
and is out of scope for the first milestone:

  • RNE's native ExecuTorch build must include the QNN delegate (stock RNE
    libs are XNNPACK/CoreML only).
  • Ship the QNN runtime .sos + Hexagon skels in the APK.
  • Wire the classification model path to load the QNN .pte.

Milestone

Milestone 1 = a validated on-device MobileNetV2 QNN .pte (top-k sane via the
runner). RNE app integration follows once the toolchain is proven.

Metadata

Metadata

Assignees

No one assigned

    Labels

    ideaNew idea to enhance the library, suggestion, etc.performanceRelated to all issues and tasks focused on improving performanceplatform: androidIssues and tasks related to Android

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions