QNN LLM Compilation Pipeline — Profiling & Optimization
- Dominant language
- Python
- Stars
- 5k
- Forks
- 1.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 581
Description
TLDR
Compiling quantized LLMs for the QNN HTP backend takes 5 to 12+ hours end-to-end. This blocks iteration on model quality, quantization recipes, and on-device evaluation. The pipeline has three major bottlenecks:
-
Wasted autoregressive generation during calibration: After prefilling a short prompt into the KV cache, the calibration loop runs
_generateto produce hundreds of tokens auto-regressively. These generated tokens are concat to the prompt and we run calibration with prompt + generated token, i don't think this improves quantization observer statistics since we use generated data by the model. -
Slow per-iteration DECODE calibration: The DECODE model processes tokens one at a time (ar_len=1), requiring 1023 sequential forward passes per wikitext chunk. Each pass takes 2-5s on CPU, making task calibration alone ~90 min. This needs deep dive into the torchao/ pytorch/ QNN quantization to find any optimizations to improve the per iteration speed.
-
QNN SDK HTP compilation:
qnn_manager.Compile(specificallyGraphFinalize) runs the HTP compiler's optimization passes at level 3 (most aggressive). This takes 102-190 min depending on model size.
Baseline Profiling
Model: qwen2_5-1_5b (hybrid, max_seq_len=1024, prefill_ar_len=128)
python examples/qualcomm/oss_scripts/llama/llama.py -b build-android --compile_only -m SM8750 \
--temperature 0 --model_mode hybrid --max_seq_len 1024 --prefill_ar_len 128 \
--decoder_model qwen2_5-1_5b \
--prompt "I would like to learn python, could you teach me with a simple example?" \
--tasks wikitext --limit 1
| Phase | Time (min) | % of Total |
|---|---|---|
| DECODE quantization | ||
| torch.export.export | 0.4 | 0.1% |
| prepare_pt2e | 0.5 | 0.1% |
| calibration (tasks) — 2 _model_calls, 1023 prefill fwd each | 122.6 | 25.3% |
| calibration (prompts) — 16-token prefill + 546 _generate fwd | 37.1 | 7.7% |
| convert_pt2e | 1.2 | 0.2% |
| save QDQ EP | 4.5 | 0.9% |
| PREFILL quantization | ||
| torch.export.export | 0.4 | 0.1% |
| prepare_pt2e | 0.7 | 0.1% |
| calibration (tasks) — 2 _model_calls, 8 prefill fwd each | 5.8 | 1.2% |
| calibration (prompts) — 16-token prefill + 546 _generate fwd | 152.0 | 31.4% |
| convert_pt2e | 2.2 | 0.5% |
| Lowering + compilation | ||
| to_edge_transform_and_lower_to_qnn (total) | 156.7 | 32.4% |
| _build_op_wrappers (kv_forward) | 7.5 | |
| _build_op_wrappers (prefill_forward) | 9.7 | |
| qnn_manager.Compile | 113.3 | |
| to_executorch + write_to_file | 0.4 | 0.1% |
| Total | ~484 (8.1h) | 100% |
Model: qwen3-1_7b (hybrid, max_seq_len=1024, prefill_ar_len=128)
| Phase | Time (min) | % of Total |
|---|---|---|
| DECODE quantization | ||
| calibration (tasks) | 86.5 | 13.3% |
| calibration (prompts) — 24-token prefill + 947 _generate fwd | 40.3 | 6.2% |
| PREFILL quantization | ||
| calibration (tasks) | 5.4 | 0.8% |
| calibration (prompts) — 24-token prefill + 947 _generate fwd | 280.5 | 43.0% |
| Lowering + compilation | ||
| qnn_manager.Compile | 190.1 | 29.1% |
| Total | ~653 (10.9h) | 100% |
Bottleneck Summary
| Bottleneck | qwen2_5-1_5b |
|---|---|
| _generate during prompt calibration | 189.1 min (39.1%) |
| DECODE task calibration (ar_len=1) | 122.6 min (25.3%) |
| qnn_manager.Compile | 113.3 min (23.4%) |
Flame graph
Notes:
- I couldn't run multiple runs per model due to long running nature and hence the percentages might have inherent overall and sub-category variance.
- I haven't verified ppl on-device, to get fast feedback created the issue with data and approaches first, to check if there is missing context.
cc @cccclai @winskuo-quic @shewu-quic @haowhsu-quic @DannyYuyang-quic @cbilgin @kimishpatel @jerryzh168
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.