Skip to main content

qnnx-backend

qnnx-backend is an experimental PyTorch torch.compile backend for Qualcomm QNN.

It enables PyTorch models to run through ONNX Runtime's QNN Execution Provider on supported Qualcomm NPU and GPU devices.

Features

  • PyTorch integration through torch.compile(..., backend="qnn")
  • Qualcomm NPU/HTP acceleration
  • Qualcomm GPU acceleration
  • Persistent QNN context caching across Python process restarts
  • Automatic NPU → GPU → CPU fallback
  • Explicit device selection
  • Operator compatibility validation
  • Static model parameter and buffer freezing
  • QNN availability detection

Requirements

  • Windows ARM64
  • Qualcomm Snapdragon platform with QNN support
  • Python 3.13

Note: Python 3.14 is not currently supported because compatible stable PyTorch wheels for Windows ARM64 are not yet available. Support can be added once PyTorch provides the required ARM64 wheels.

Tested configuration

The following versions are covered by the compatibility test matrix:

  • Python 3.13
  • PyTorch 2.13 and 2.14
  • ONNX Runtime QNN 2.4, 2.5 and 2.6
  • Snapdragon X Elite

Installation

qnnx-backend currently supports Python 3.13 ARM64 on Windows ARM64.

Important: make sure you are using a native ARM64 Python installation.
A Python x64 installation will not use the required Windows ARM64 wheels.

Python.org provides dedicated Windows ARM64 installers for supported Python versions.

You can verify your Python architecture with:

python -c "import platform; print(platform.machine())"

The output should be:

ARM64

Then install PyTorch:

python -m pip install torch==2.14.0+cpu --index-url https://download.pytorch.org/whl/cpu

Finally, install qnnx-backend:

python -m pip install qnnx-backend

Usage

Basic usage

qnnx_backend integrates with PyTorch through torch.compile.

import torch
import qnnx_backend

model = MyModel().eval()
x = torch.randn(1, 32)

compiled_model = torch.compile(
    model,
    backend="qnn",
)

with torch.inference_mode():
    output = compiled_model(x)

By default, qnnx_backend automatically selects an available QNN accelerator.

Checking QNN availability

You can check whether at least one QNN accelerator is available:

qnnx_backend.is_available()

You can also check a specific device:

qnnx_backend.is_available("npu")
qnnx_backend.is_available("gpu")

Device selection

The QNN backend supports the following device options:

  • "auto" — automatically selects an available accelerator
  • "npu" — uses the Qualcomm NPU/HTP backend
  • "gpu" — uses the Qualcomm GPU backend
  • "cpu" — uses the original PyTorch graph without QNN acceleration

The default is "auto".

compiled_model = torch.compile(
    model,
    backend="qnn",
    options={
        "device": "npu",
    },
)

Backend options

Option Values Default Description
device auto, npu, gpu, cpu auto Selects the execution backend.
strict True, False False Controls whether compilation failures can fall back.
fp16 True, False False Enables HTP FP16 precision for NPU execution.
cache True, False False Enables persistent QNN context caching across process restarts.
cache_dir path platform default Overrides the persistent context cache directory.

Automatic fallback

With the default configuration, qnnx_backend falls back when QNN compilation is not possible.

In "auto" mode, devices are attempted in the following order:

NPU -> GPU -> CPU

For example:

compiled_model = torch.compile(
    model,
    backend="qnn",
)

If the graph cannot be compiled for the NPU, the GPU is attempted. If neither QNN accelerator can execute the graph, PyTorch CPU execution is used.

Strict mode

Use strict=True when execution on the selected QNN device is required:

compiled_model = torch.compile(
    model,
    backend="qnn",
    options={
        "device": "npu",
        "strict": True,
    },
)

In strict mode, a QNN compilation or session creation failure is propagated instead of falling back to CPU.

This is useful when testing whether a graph is actually supported by a specific QNN backend.

NPU FP16 execution

For NPU execution, FP16 precision can be enabled with:

compiled_model = torch.compile(
    model,
    backend="qnn",
    options={
        "device": "npu",
        "fp16": True,
    },
)

This option controls the QNN HTP FP16 precision setting.

Persistent context cache

QNN graph compilation can introduce a significant startup cost.

Persistent context caching allows qnnx_backend to reuse a previously compiled QNN context across Python process restarts:

compiled_model = torch.compile(
    model,
    backend="qnn",
    options={
        "device": "npu",
        "cache": True,
    },
)

On the first execution, the QNN context is compiled and stored on disk. Subsequent executions of the same model and configuration can reuse the cached context instead of compiling it again.

Persistent context caching is supported for both QNN NPU and GPU execution.

A custom cache directory can be specified with:

compiled_model = torch.compile(
    model,
    backend="qnn",
    options={
        "device": "npu",
        "cache": True,
        "cache_dir": "./qnn-cache",
    },
)

The cache key includes the exported model and relevant QNN compilation configuration, so incompatible contexts are stored separately.

Inference

qnnx_backend currently targets inference workloads.

Models should be switched to evaluation mode before compilation:

model.eval()

Inference should normally be performed using:

with torch.inference_mode():
    output = compiled_model(x)

Training and backward execution are not currently supported.

Model parameters and buffers

Model parameters and buffers are captured when the QNN graph is compiled.

For example:

compiled_model = torch.compile(
    model.eval(),
    backend="qnn",
)

compiled_model(x)

After compilation, modifying parameters or buffers of the original model does not update the existing QNN session.

Recompile the model after changing its parameters:

compiled_model = torch.compile(
    model.eval(),
    backend="qnn",
)

Static shapes

qnnx_backend currently targets static input shapes.

Compile the model using the input shapes that will be used during inference.

Dynamic-shape support is not currently provided.

Operator support

qnnx_backend validates exported ONNX operators against the currently supported QNN backends.

Operator support may differ between NPU/HTP and GPU.

See the operator compatibility list for the current support matrix.

Benchmarks

Benchmarks were performed on Windows ARM64 with a Snapdragon X Elite.

The results below show steady-state inference latency after warm-up.

Small models

Steady-state inference latency after warm-up. Values are median latency in milliseconds (lower is better).

Model Batch PyTorch CPU QNN GPU QNN NPU
Small MLP 16 0.042 ms 0.201 ms 0.203 ms
Small CNN 16 0.299 ms 0.953 ms 0.212 ms
Small Transformer 16 0.750 ms 1.459 ms 0.306 ms

Medium models

Steady-state inference latency after warm-up. Values are median latency in milliseconds (lower is better).

Model Batch PyTorch CPU QNN GPU QNN NPU
Medium MLP 16 2.523 ms 0.505 ms 0.333 ms
Medium CNN 16 134.117 ms 11.292 ms 2.143 ms
Medium Transformer 16 47.343 ms 9.176 ms 2.036 ms

Persistent context cache

Persistent context caching also reduces time to first inference across Python process restarts:

Device Cold startup Warm startup Reduction
NPU 3.792 s 2.352 s 38.0%
GPU 3.886 s 2.010 s 48.3%

Each warm measurement was performed in a fresh Python process using a context generated during the corresponding cold run.

The benchmark documentation provides detailed results for the synthetic architectures above and for three Hugging Face transformer models, together with the methodology, coverage and limitations.

Author

Developed by Thomas Le Gall — ORCID · GitLab.

License

This project is licensed under the MIT License. See the LICENSE file for details.

Copyright © 2026 Thomas Le Gall.

Metadata

Release files for qnnx-backend 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for qnnx-backend 0.3.0
File Size Uploaded
qnnx_backend-0.3.0.tar.gz 20.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for qnnx-backend 0.3.0
File Interpreter ABI Platform
qnnx_backend-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 42.0 kB

Release files / qnnx_backend-0.3.0.tar.gz

Download URL qnnx_backend-0.3.0.tar.gz
Size 20.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1be6bd90e5dbf0f5840e8755756e5c073c63c93a14ac1346e5c80661312f1afb
BLAKE2b-256 checksum
How to use checksums
bb9517343357f9c1830999c10dea45b1e7145b3a1a7c7c1ccf11854391ce120c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.15

Release files / qnnx_backend-0.3.0-py3-none-any.whl

Download URL qnnx_backend-0.3.0-py3-none-any.whl
Size 21.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
69e1419084966b53167a731b5a451a4a81e1bcbbe0e8568d5549fd8887c4697d
BLAKE2b-256 checksum
How to use checksums
abd8cba80d23a7159e45eeae6f48250aac8cb457a3a5597bef610c9bd56dd0fd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.15

Release history Release notifications | RSS feed

0.4.0

2 release files

This release

0.3.0 This release

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page