QHexRT launch article: full-stack NPU inference, not open source
Browse files
blog/qhexrt-npu-vs-cpu-lfm2.5-230m.md
CHANGED
|
@@ -1,42 +1,41 @@
|
|
| 1 |
-
# QHexRT:
|
| 2 |
|
| 3 |
-
We shipped MetalRT for Apple Silicon
|
| 4 |
|
| 5 |
-
**QHexRT runs on
|
| 6 |
|
| 7 |
-
|
| 8 |
|
| 9 |
-
|
| 10 |
|
| 11 |
-
|
| 12 |
|
| 13 |
-
|
| 14 |
|
| 15 |
-
|
| 16 |
-
- **Time-to-first-token: 36ms flat** for any prompt up to 512 tokens. The CPU takes 588ms at 512 tokens.
|
| 17 |
-
- **Decode: the CPU wins** at 250 tok/s vs 172 on the NPU. At 230M params, decode is memory-bandwidth-bound, and the Oryon CPU pulls more usable bandwidth from DDR on this weight set.
|
| 18 |
-
- **End-to-end: the NPU wins** once the prompt exceeds ~1.5x the generation length.
|
| 19 |
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
-
|
| 23 |
|
| 24 |
-
|
| 25 |
-
| --- | --- |
|
| 26 |
-
| **Device** | Qualcomm SM8850 (Snapdragon 8 Elite Gen-2 class) |
|
| 27 |
-
| **NPU** | Hexagon v81 (HTP) |
|
| 28 |
-
| **CPU** | 8-core Oryon (up to ~4.4 GHz) |
|
| 29 |
-
| **Model** | [LiquidAI/LFM 2.5 230M](https://huggingface.co/LiquidAI/LFM2.5-230M) (229.69M params, hybrid decoder) |
|
| 30 |
-
| **NPU bundle** | [runanywhere/lfm2_5_230m_HNPU](https://huggingface.co/runanywhere/lfm2_5_230m_HNPU) (v81 context binaries, W8) |
|
| 31 |
-
| **NPU stack** | QHexRT, W8 weight-only (int8 weights, fp16 activations) |
|
| 32 |
-
| **CPU stack** | llama.cpp b1-beac530, Q8_0 + Q4_K_M |
|
| 33 |
-
| **Fairness** | HTP clock pinned to TURBO; CPU governor pinned to performance, all 8 cores |
|
| 34 |
|
| 35 |
-
|
| 36 |
|
| 37 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
|
| 39 |
-
|
| 40 |
|
| 41 |
```bash
|
| 42 |
hf download runanywhere/lfm2_5_230m_HNPU --local-dir lfm2_5_230m_HNPU
|
|
@@ -45,86 +44,49 @@ adb shell "cd /data/local/tmp/lfm230 && LD_LIBRARY_PATH=. \
|
|
| 45 |
./qhx_generate lfm2-5-230m.json libQnnHtp.so libQnnSystem.so . 64 'The capital of France is'"
|
| 46 |
```
|
| 47 |
|
| 48 |
-
Stage the QAIRT v81 runtime libs
|
| 49 |
-
|
| 50 |
-
Greedy output matches the source model: `"The capital of France is"` β `" Paris."`
|
| 51 |
|
| 52 |
## Throughput
|
| 53 |
|
| 54 |
-
| Engine | Prefill (tok/s) | Decode (tok/s) |
|
| 55 |
-
| --- | ---: | ---: | ---: |
|
| 56 |
-
| **QHexRT NPU** (v81, W8) | **12,540** | 172 |
|
| 57 |
-
| **llama.cpp CPU** Q8_0 | 871 | **250** |
|
| 58 |
-
| **llama.cpp CPU** Q4_K_M | 680 | **264** |
|
| 59 |
-
|
| 60 |
-
Prefill is where the NPU wins by the widest margin. QHexRT runs it as one batched forward over a padded 512-token window, so the cost stays constant regardless of prompt length.
|
| 61 |
|
| 62 |
-

|
| 77 |
-
|
| 78 |
-
**Figure 2 β Time to first token vs prompt length.** The NPU holds a flat **~36 ms** TTFT for any prompt up to 512 tokens because prefill runs as one batched forward pass. The CPU must stream every prompt token through the weights, so TTFT grows linearly. At 512 tokens the NPU delivers its first token **16x sooner** than Q8_0 (36 ms vs 588 ms). For prompts under ~30 tokens the CPU is still faster; past that crossover the NPU wins by a widening margin.
|
| 79 |
-
|
| 80 |
-
## End-to-end latency
|
| 81 |
-
|
| 82 |
-
End-to-end combines a one-time TTFT with G decode steps:
|
| 83 |
-
|
| 84 |
-
| Prompt (tokens) | NPU (ms) | CPU Q8_0 (ms) | CPU Q4_K_M (ms) |
|
| 85 |
-
| ---: | ---: | ---: | ---: |
|
| 86 |
-
| 16 | 773 | **559** | **513** |
|
| 87 |
-
| 128 | 773 | **674** | 675 |
|
| 88 |
-
| 512 | **773** | 1130 | 1239 |
|
| 89 |
-
|
| 90 |
-
At G=128, the NPU holds constant at 773ms regardless of prompt length. The CPU starts faster on short prompts but its rising prefill cost overtakes the NPU near a ~200-token prompt. A real run (15-token prompt + 128 generated tokens) measured 777ms. The formula predicted 772ms. 1% error.
|
| 91 |
|
| 92 |
-
|
| 93 |
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
**Use QHexRT (NPU)** for prefill-heavy or latency-sensitive workloads: RAG, summarization, long-context requests, classification, extraction, scoring. The flat TTFT keeps the first token fast regardless of prompt length. The NPU also frees the CPU for the rest of the app and uses less power.
|
| 97 |
-
|
| 98 |
-
**Use the CPU** for short-prompt, long-generation chat on tiny models (under ~500M params), where raw decode tok/s dominates and prompts stay well under the crossover.
|
| 99 |
-
|
| 100 |
-
Peak RAM: QHexRT NPU 445 MB, llama.cpp Q8_0 299 MB, Q4_K_M 209 MB. The NPU keeps int8 weights, the QNN context, and persistent graph I/O tensors resident.
|
| 101 |
|
| 102 |
## The model catalog
|
| 103 |
|
| 104 |
-
[LFM 2.5 230M on Hexagon v81](https://huggingface.co/runanywhere/lfm2_5_230m_HNPU) is the first
|
| 105 |
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
Next on the roadmap:
|
| 109 |
-
|
| 110 |
-
- **More models.** Qwen3, Gemma 3, Phi-4-mini, and other sub-4B models that fit the NPU's memory budget.
|
| 111 |
-
- **W4 quantization.** A working W4 path would roughly halve the per-token bytes, projecting ~300-340 tok/s decode.
|
| 112 |
-
- **Power metering.** The CPU numbers come from 8 Oryon cores pinned at max clock. The NPU does the same work at a fraction of the power.
|
| 113 |
|
| 114 |
## Summary
|
| 115 |
|
| 116 |
-
- **
|
| 117 |
-
- **
|
| 118 |
-
- **
|
| 119 |
-
- **
|
| 120 |
-
- **
|
| 121 |
-
-
|
| 122 |
-
- Identical greedy output, W8 logits cosine 1.000000 vs fp32 oracle
|
| 123 |
-
|
| 124 |
-
LFM 2.5 230M is the first model in the QHexRT catalog. More models, more SoCs, more benchmarks are coming.
|
| 125 |
|
| 126 |
-
|
| 127 |
|
| 128 |
---
|
| 129 |
|
| 130 |
-
_Benchmarked on Qualcomm SM8850 / Hexagon v81. Model: LiquidAI/LFM 2.5 230M. NPU: QHexRT W8 weight-only, QAIRT 2.47.0.260601.
|
|
|
|
| 1 |
+
# QHexRT Is Live: Full-Stack NPU Inference for Qualcomm Hexagon
|
| 2 |
|
| 3 |
+
We shipped MetalRT for Apple Silicon β the first engine to run LLM, speech, vision, and speech-to-speech in one runtime, entirely on the GPU. Today we're launching **QHexRT** for Qualcomm: the same bet on the NPU.
|
| 4 |
|
| 5 |
+
**QHexRT runs inference 100% on the Hexagon NPU.** No Python in the hot path. No CPU fallback during inference. We are building the widest model catalog on any Qualcomm NPU stack, with same-day support for the models the community ships.
|
| 6 |
|
| 7 |
+
No one has shipped a single runtime that covers **LLM, VLM, STT, TTS, and embeddings** fully on Qualcomm NPUs. MetalRT did it for Apple Silicon. QHexRT does it for Hexagon.
|
| 8 |
|
| 9 |
+
## First model: LFM 2.5 230M
|
| 10 |
|
| 11 |
+
**LiquidAI released [LFM 2.5 230M](https://huggingface.co/LiquidAI/LFM2.5-230M) on June 25, 2026.** QHexRT supports it on day one β our first catalog entry. The NPU bundle is here: [runanywhere/lfm2_5_230m_HNPU](https://huggingface.co/runanywhere/lfm2_5_230m_HNPU).
|
| 12 |
|
| 13 |
+
Every tensor in the inference path stays on the HTP: decode graph, prefill graph, lm-head, embeddings. Greedy output matches the source model (`"The capital of France is"` β `" Paris."`).
|
| 14 |
|
| 15 |
+
## All modalities, one NPU runtime
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
| Modality | Status |
|
| 18 |
+
| --- | --- |
|
| 19 |
+
| **LLM** | Live β LFM 2.5 230M on Hexagon v81 |
|
| 20 |
+
| **VLM** | In development |
|
| 21 |
+
| **STT** | In development |
|
| 22 |
+
| **TTS** | In development |
|
| 23 |
+
| **Embeddings** | In development |
|
| 24 |
|
| 25 |
+
One engine, one deployment model, every modality on the NPU β the same architecture MetalRT proved on Apple Silicon, now on Qualcomm.
|
| 26 |
|
| 27 |
+
## The headline numbers (LFM 2.5 230M Β· Hexagon v81)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
+
Benchmarked on **Hexagon v81** (Snapdragon 8 Elite Gen-2, SM8850). We also ran llama.cpp on the CPU of the same chip, same die, same phone. Both stacks produce identical output.
|
| 30 |
|
| 31 |
+
- **Prefill: 12,540 tok/s** on the NPU vs 871 on the CPU (Q8_0). **14.4x faster.**
|
| 32 |
+
- **Time-to-first-token: 36ms flat** for any prompt up to 512 tokens. The CPU takes 588ms at 512 tokens.
|
| 33 |
+
- **Decode: the CPU wins** at 250 tok/s vs 172 on the NPU. At 230M params, decode is memory-bandwidth-bound.
|
| 34 |
+
- **End-to-end: the NPU wins** once the prompt exceeds ~1.5x the generation length.
|
| 35 |
+
|
| 36 |
+
QHexRT's W8 graph matched the HuggingFace fp32 oracle at logits cosine 1.000000.
|
| 37 |
|
| 38 |
+
## Run it
|
| 39 |
|
| 40 |
```bash
|
| 41 |
hf download runanywhere/lfm2_5_230m_HNPU --local-dir lfm2_5_230m_HNPU
|
|
|
|
| 44 |
./qhx_generate lfm2-5-230m.json libQnnHtp.so libQnnSystem.so . 64 'The capital of France is'"
|
| 45 |
```
|
| 46 |
|
| 47 |
+
Stage the QAIRT v81 runtime libs and the `qhx_generate` tool into the same directory. Context binaries are pinned to Hexagon v81.
|
|
|
|
|
|
|
| 48 |
|
| 49 |
## Throughput
|
| 50 |
|
| 51 |
+
| Engine | Prefill (tok/s) | Decode (tok/s) | Peak RAM (MB) |
|
| 52 |
+
| --- | ---: | ---: | ---: |
|
| 53 |
+
| **QHexRT NPU** (v81, W8) | **12,540** | 172 | 445 |
|
| 54 |
+
| **llama.cpp CPU** Q8_0 | 871 | **250** | 299 |
|
| 55 |
+
| **llama.cpp CPU** Q4_K_M | 680 | **264** | 209 |
|
|
|
|
|
|
|
| 56 |
|
| 57 |
+

|
| 58 |
|
| 59 |
+
**Figure 1 β Prefill throughput (log scale).** QHexRT on Hexagon v81 hits **12,540 tok/s** prefill on LFM 2.5 230M. **14.4x** faster than llama.cpp CPU Q8_0 on the same die.
|
|
|
|
|
|
|
| 60 |
|
| 61 |
## Time to first token
|
| 62 |
|
| 63 |
+
| Prompt (tokens) | NPU (ms) | CPU Q8_0 (ms) |
|
| 64 |
+
| ---: | ---: | ---: |
|
| 65 |
+
| 16 | **36** | 17 |
|
| 66 |
+
| 128 | **36** | 132 |
|
| 67 |
+
| 512 | **36** | 588 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 68 |
|
| 69 |
+

|
| 70 |
|
| 71 |
+
**Figure 2 β Time to first token (LFM 2.5 230M).** Flat **~36 ms** TTFT on the NPU for any prompt up to 512 tokens.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
## The model catalog
|
| 74 |
|
| 75 |
+
[LFM 2.5 230M on Hexagon v81](https://huggingface.co/runanywhere/lfm2_5_230m_HNPU) is the first entry. We're adding models as fast as the community ships them.
|
| 76 |
|
| 77 |
+
Next: **VLM, STT, TTS, embeddings** on Hexagon, more LLM models, W4 quantization, and power metering.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
## Summary
|
| 80 |
|
| 81 |
+
- **QHexRT is live** β full-stack NPU inference for Qualcomm Hexagon
|
| 82 |
+
- **100% on NPU** β no Python, no CPU fallback during inference
|
| 83 |
+
- **First model:** LFM 2.5 230M ([runanywhere/lfm2_5_230m_HNPU](https://huggingface.co/runanywhere/lfm2_5_230m_HNPU))
|
| 84 |
+
- **12,540 tok/s** prefill Β· **36ms** flat TTFT
|
| 85 |
+
- **All modalities** β LLM live; VLM, STT, TTS, embeddings in development
|
| 86 |
+
- **Widest model catalog** for Qualcomm NPUs, expanding continuously
|
|
|
|
|
|
|
|
|
|
| 87 |
|
| 88 |
+
Full write-up: [runanywhere.ai/blog](https://www.runanywhere.ai/blog/qhexrt-npu-vs-cpu-lfm2.5-230m-qualcomm-hexagon)
|
| 89 |
|
| 90 |
---
|
| 91 |
|
| 92 |
+
_Benchmarked on Qualcomm SM8850 / Hexagon v81. Model: LiquidAI/LFM 2.5 230M. NPU: QHexRT W8 weight-only, QAIRT 2.47.0.260601._
|