char-fork/llama.cpp
git clone https://git.t4t.associates/char-fork/llama.cpp
eafe15a5e
char/aliceai-t5-moe
Snapdragon-based devices
Setup
The cross-compilation toolchain images are provided by the Qualcomm Snapdragon Toolchain registry. These Docker images include the Android NDK, OpenCL SDK, Hexagon SDK, CMake, and the necessary cross-compilers:
- Android toolchain:
ghcr.io/snapdragon-toolchain/arm64-android:v0.7 - Linux toolchain:
ghcr.io/snapdragon-toolchain/arm64-linux:v0.7
The unified build utility (scripts/snapdragon/build.py) automatically pulls
and orchestrates these containers to perform target compilation.
You only need to ensure that Docker (or Docker Desktop on macOS/Windows) is running on your host machine.
Specific setup, build, and installation details for Linux and Windows on Snapdragon platforms are documented in:
How to Build
Using build.py script (Recommended)
The easiest way to build llama.cpp is by using the scripts/snapdragon/build.py script. It automatically copies the CMake presets,
launches the correct compilation Docker container, builds the libraries and tools,
installs them, and optionally pushes them to your ADB device.
Build and deploy for Android target (accepts android or adb alias):
$ ./scripts/snapdragon/build.py --target adb --push
Build and deploy for Linux target (accepts linux or lnx alias):
$ ./scripts/snapdragon/build.py --target linux:user@host --push
Manual CMake Build
Alternatively, you can build llama.cpp manually by entering the cross-compilation Docker container and running the CMake commands:
# Start the cross-compilation container manually: ~/src/llama.cpp $ docker run-it --rm -u $( id -u ) :$( id -g ) --volume $( pwd ) :/workspace--platform linux/amd64 ghcr.io/snapdragon-toolchain/arm64-android:v0.7# Inside the container, build the project using presets: [d]/workspace> cp docs/backend/snapdragon/CMakeUserPresets.json . [d]/workspace> cmake --preset arm64-android-snapdragon-release -B build-snapdragonPreset CMake variables:ANDROID_ABI ="arm64-v8a" ... CMAKE_TOOLCHAIN_FILE ="/opt/android-ndk-r28b/build/cmake/android.toolchain.cmake" GGML_HEXAGON ="ON" GGML_OPENCL ="ON" GGML_OPENMP ="OFF" HEXAGON_SDK_ROOT ="/opt/hexagon/6.6.0.0" ... -- Including OpenCL backend-- Including Hexagon backend... -- Build files have been written to: /workspace/build-snapdragon [d]/workspace> cmake --build build-snapdragon... [144/356]Performing build step for'htp-v73' [1/16]Generating htp_iface_skel.c, htp_iface_stub.c, htp_iface.h [2/16]Building C object CMakeFiles/ggml-htp-v73.dir/hvx-sigmoid.c.obj [3/16]Building C object CMakeFiles/ggml-htp-v73.dir/htp-dma.c.obj [4/16]Building C object CMakeFiles/ggml-htp-v73.dir/worker-pool.c.obj... -- Installing: /workspace/build-snapdragon/ggml/src/ggml-hexagon/libggml-htp-v73.so-- Installing: /workspace/build-snapdragon/ggml/src/ggml-hexagon/libggml-htp-v75.so...
To generate an installable "package" simply use cmake --install:
[d]/workspace> cmake --install build-snapdragon --prefix pkg-android/llama.cpp -- Install configuration: "Release" -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-cpu.so -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-opencl.so -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-hexagon.so -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v73.so -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v75.so -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v79.so -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v81.so -- Installing: /workspace/pkg-android/llama.cpp/lib/libggml.so ... -- Installing: /workspace/pkg-android/llama.cpp/bin/llama-bench -- Installing: /workspace/pkg-android/llama.cpp/bin/llama-cli ...
How to Install
Android
For this step, your device needs to be configured for on-device development. Please see https://developer.android.com/studio/debug/dev-options for details.
Once ADB is enabled, use adb push to install pkg-android on the device.
Note that the toolchain Docker image doesn't have ADB and doesn't set up the ADB bridge. Please use native ADB on the host.
~/src/llama.cpp$ adb push pkg-android/llama.cpp /data/local/tmp/ pkg-android/llama.cpp/bin/: 67 files pushed, 0 skipped. 190.2 MB/s (919095042 bytes in 4.607s) pkg-android/llama.cpp/include/: 19 files pushed, 0 skipped. 20.5 MB/s (255173 bytes in 0.012s) pkg-android/llama.cpp/lib/: 16 files pushed, 0 skipped. 144.4 MB/s (43801382 bytes in 0.289s) 102 files pushed, 0 skipped. 186.9 MB/s (963151597 bytes in 4.914s)
At this point, you should also install some models:
~/src/llama.cpp$ wget https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_0.gguf ... 2025-10-11 12:04:52 (10.7 MB/s) - ‘Llama-3.2-1B-Instruct-Q4_0.gguf’ saved [773025920/773025920] ~/src/llama.cpp$ adb push Llama-3.2-1B-Instruct-Q4_0.gguf /data/local/tmp/gguf Llama-3.2-1B-Instruct-Q4_0.gguf: 1 file pushed, 0 skipped. 38.3 MB/s (773025920 bytes in 19.250s)
Windows
All artifacts are already installed in the pkg-wos folder.
To run, you can use the scripts/snapdragon/run.py runner script (see details below).
How to Run
The easiest way to run llama.cpp cli tools is using the provided scripts/snapdragon/run.py wrapper script. This script automatically
maps CLI options to environment variables, resolves executable paths, and runs the command locally, via ADB, or remotely via SSH on the
target device.
llama.cpp supports three backends on Snapdragon-based devices: CPU, Adreno GPU (GPUOpenCL), and Hexagon NPU.
You can select which backend(s) to run the model on using the --device option of the tool (or --devices option in run.py).
Hexagon NPU behaves as a "GPU" device when it comes to -ngl and other offload-related options.
Here are some examples of running various llama.cpp tools.
Generating a completion with Gemma on Android (relying on default HTP0:0 device and default thread count -t 6):
~/src/llama.cpp$ ./scripts/snapdragon/run.py --target adb -- llama-completion -m models/gemma-2-2b-it-Q4_0.gguf -f prompts/sample_prompt_1024.txt --jinja -st ... ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev 1 ggml-hex: Hexagon Arch version v79 ggml-hex: allocating new session: HTP0:0 ... load_tensors: offloading output layer to GPU load_tensors: offloaded 27/27 layers to GPU load_tensors: CPU model buffer size = 300.00 MiB load_tensors: HTP0:0 model buffer size = 1400.26 MiB ... llama_perf_context_print: prompt eval time = 320.00 ms / 1024 tokens ( 0.31 ms per token, 3200.00 tokens per second) llama_perf_context_print: eval time = 2100.00 ms / 100 runs ( 21.00 ms per token, 47.62 tokens per second)
Simple question for Llama-3.2-1B:
~/src/llama.cpp$ ./scripts/snapdragon/run.py --target android --devices HTP0 -- llama-cli -m Llama-3.2-1B-Instruct-Q4_0.gguf -p "what is the most popular cookie in the world?" ... ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev 1 ggml-hex: Hexagon Arch version v79 ggml-hex: allocating new session: HTP0 ggml-hex: new session: HTP0 : session-id 0 domain-id 3 uri file:///libggml-htp-v79.so?htp_iface_skel_handle_invoke&_modver=1.0&_dom=cdsp&_session=0 handle 0xb4000072c7955e50 ... load_tensors: offloading output layer to GPU load_tensors: offloaded 17/17 layers to GPU load_tensors: CPU model buffer size = 225.49 MiB load_tensors: HTP0 model buffer size = 504.26 MiB ... I hope this helps you understand the world's most popular cookies! [end of text] ... llama_perf_sampler_print: sampling time = 30.08 ms / 487 runs ( 0.06 ms per token, 16191.77 tokens per second) llama_perf_context_print: load time = 617.94 ms llama_perf_context_print: prompt eval time = 80.76 ms / 11 tokens ( 7.34 ms per token, 136.21 tokens per second) llama_perf_context_print: eval time = 9210.59 ms / 475 runs ( 19.39 ms per token, 51.57 tokens per second) llama_perf_context_print: total time = 9454.92 ms / 486 tokens llama_perf_context_print: graphs reused = 473 llama_memory_breakdown_print: | memory breakdown [MiB] | total free self model context compute unaccounted | llama_memory_breakdown_print: | - HTP0 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | llama_memory_breakdown_print: | - Host | 439 = 225 + 136 + 77 |
Op test for MUL_MAT:
~/src/llama.cpp$ ./scripts/snapdragon/run.py --target adb --devices HTP0:0 -- test-backend-ops -b HTP0:0 -o MUL_MAT ... Backend 2/3: HTP0:0 Device description: Hexagon Device memory: 2048 MB (2048 MB free) MUL_MAT(type_a=q4_0,type_b=f32,m=16,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],v=0,o=1): OK MUL_MAT(type_a=q4_0,type_b=f32,m=16,n=2,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],v=0,o=1): OK MUL_MAT(type_a=q4_0,type_b=f32,m=16,n=3,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],v=0,o=1): OK
Llama benchmark:
~/src/llama.cpp$ ./scripts/snapdragon/run.py --target adb --devices HTP0 -- llama-bench -p 128 -n 64 -m Llama-3.2-1B-Instruct-Q4_0.gguf ... ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev 1 ggml-hex: Hexagon Arch version v79 ggml-hex: allocating new session: HTP0 ggml-hex: new session: HTP0 : session-id 0 domain-id 3 uri file:///libggml-htp-v79.so?htp_iface_skel_handle_invoke&_modver=1.0&_dom=cdsp&_session=0 handle 0xb400007d4b231090 | model | size | params | backend | ngl | threads | n_batch | mmap | test | t/s | | ---------------| ---------: | -----: | ---------- | --: | ------: | ------: | ---: | ----: | ------------: | | llama 1B Q4_0 | 729.75 MiB | 1.24 B | HTP | 99 | 4 | 128 | 0 | pp128 | 169.42 ± 1.75 | | llama 1B Q4_0 | 729.75 MiB | 1.24 B | HTP | 99 | 4 | 128 | 0 | tg64 | 51.54 ± 1.13 |
Multi-Device Execution Modes
The Hexagon backend supports multiple execution and partitioning modes to accommodate different model sizes, memory constraints, and single- or multi-NPU hardware topologies:
1. Single-Device Mode with Dynamic Buffer Mapping
Runs the model on a single NPU session (e.g. HTP0 or HTP0:0).
A single NPU session provides ~3.5GB of available virtual address space. For models larger than 3.5GB, the backend automatically maps and unmaps weight buffers during graph execution. This allows large models to run on a single NPU without manual configuration:
./scripts/snapdragon/run.py --target adb--devices HTP0:0-- \ llama-cli-m models/Llama-3.2-3B-Instruct-Q4_0.gguf-ngl 99-p "Hello"
2. Layer-Split Mode across Virtual Sessions (HTP0,HTP1,... or HTP0:0,HTP0:1,...)
Partitions model layers at load time across multiple virtual sessions hosted on a single physical NPU.
Each virtual session acts as an independent backend device from llama.cpp's perspective (similar to multiple GPUs). Because layers are permanently distributed across sessions, each session's allocated weights remain within its private 3.5GB address space window, eliminating runtime buffer re-mapping overhead.
Here is an example of running the GPT-OSS-20B model on a Snapdragon device using 4 virtual sessions on a single NPU:
./scripts/snapdragon/run.py --target adb \--devices HTP0:0,HTP0:1,HTP0:2,HTP0:3-- \ llama-cli--load-mode none-m /data/local/tmp/gguf/gpt-oss-20b-Q4_0.gguf-t 4 \--ctx-size 8192--batch-size 128-ctk q8_0-ctv q8_0-fa on-ngl 99-no-cnv -f surfing.txt
Log output snippet:
... llama_model_loader: - type f32: 289 tensors llama_model_loader: - type q4_0: 96 tensors llama_model_loader: - type q8_0: 2 tensors llama_model_loader: - type mxfp4: 72 tensors ... load_tensors: offloaded 25/25 layers to GPU load_tensors: CPU model buffer size = 1182.09 MiB load_tensors: HTP0:1 model buffer size = 2512.58 MiB load_tensors: HTP0:3 model buffer size = 2093.83 MiB load_tensors: HTP0:0 model buffer size = 2931.34 MiB load_tensors: HTP0:2 model buffer size = 2512.58 MiB ... llama_perf_context_print: prompt eval time = 3843.67 ms / 197 tokens ( 19.51 ms per token, 51.25 tokens per second) llama_perf_context_print: eval time = 1686.13 ms / 31 runs ( 54.39 ms per token, 18.39 tokens per second) llama_perf_context_print: total time = 6266.30 ms / 228 tokens llama_memory_breakdown_print: | memory breakdown [MiB] | total free self model context compute unaccounted | llama_memory_breakdown_print: | - HTP0:0 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | llama_memory_breakdown_print: | - HTP0:1 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | llama_memory_breakdown_print: | - HTP0:2 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | llama_memory_breakdown_print: | - HTP0:3 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | llama_memory_breakdown_print: | - Host | 1476 = 1208 + 105 + 162 |
3. Tensor-Split Mode across Physical Devices (HTP0:0,HTP1:0,...)
Distributes model tensors across distinct physical NPU hardware cores using llama.cpp's tensor parallelism
(--split-mode tensor).
Tensors are partitioned across physical NPUs for parallel execution (proportions are distributed equally by default without
needing an explicit --tensor-split option):
./scripts/snapdragon/run.py --target adb \--devices HTP0:0,HTP1:0-- \ llama-cli-m models/Llama-3.2-3B-Instruct-Q4_0.gguf--split-mode tensor-ngl 99-p "Hello"
4. Row-Split Multi-Device Mode via Device Grouping (HTP0[0-1])
Groups multiple physical NPU cores into a single logical device using bracket notation (HTP0[0-1] or HTP0[0,1]).
Unlike host-level tensor-splitting, row-splitting is executed entirely inside the Hexagon backend:
./scripts/snapdragon/run.py --target adb \--devices 'HTP0[0-1]' -- \ llama-cli-m models/Llama-3.2-3B-Instruct-Q4_0.gguf-ngl 99-p "Hello"
You can also combine row-splitting with layer-splitting across multiple grouped devices (e.g. --devices 'HTP0[0-1],HTP1[2-3]'
on 4 physical NPUs, or --devices 'HTP0[0-1:0],HTP1[0-1:1]' on 2 physical NPUs using virtual sessions 0 and 1).
Environment variables
-
GGML_HEXAGON_DEVICES(default: not set, defaults to HTP0 session) Controls which NPU devices and sessions to allocate. Configurable via--devicesinrun.py:N(single integer): AllocatesNvirtual sessions namedHTP0,HTP1, ...,HTP<N-1>on physical NPU 0.HTP<phys>:<virt>,...: Comma-separated list of individual devices specifying physical and virtual index:HTP0:0,HTP0:1: Two virtual sessions on physical NPU 0 (layer-split on single NPU).HTP0:0,HTP1:0: One session on physical NPU 0 and one on physical NPU 1 (tensor-split across physical cores).
HTP<name>[<phys_spec>]: Device grouping syntax for row-split multi-device execution:HTP0[0-1]: A single logical deviceHTP0that groups physical cores 0 and 1.HTP0[0-1],HTP1[2-3]: Two layer-split devices across 4 physical NPUs (cores 0-1 and 2-3).HTP0[0-1:0],HTP1[0-1:1]: Two layer-split devices across 2 physical NPUs using virtual sessions 0 and 1.
-
GGML_HEXAGON_NDEV(deprecated) Replaced byGGML_HEXAGON_DEVICES. Controls the number of virtual sessions to allocate on physical NPU0. Allocates sessions namedHTP0,HTP1, etc. -
GGML_HEXAGON_NHVX=0Controls the number of HVX hardware threads to use. The default is all (actual number varies depending on the hardware version). -
GGML_HEXAGON_HOSTBUF=1(default: 0, disabled) Enables allocating host buffers for debugging. By default, host buffers are disabled. -
GGML_HEXAGON_VERBOSE=1Enables verbose logging of Ops from the backend. Example output:ggml-hex: HTP0 graph-compute n_nodes 2 ggml-hex: HTP0 matmul : blk.27.ffn_up.weight x ffn_norm-27 -> ffn_up-27 : 3072:8192 x 3072:1 -> 8192:1 : q4_0 x f32 -> f32 : HTP0 x HTP0 -> HTP0 : flags 0x1 ggml-hex: HTP0 matmul : blk.27.ffn_gate.weight x ffn_norm-27 -> ffn_gate-27 : 3072:8192 x 3072:1 -> 8192:1 : q4_0 x f32 -> f32 : HTP0 x HTP0 -> HTP0 : flags 0x3 ggml-hex: HTP0 graph-compute n_nodes 1 ggml-hex: HTP0 matmul : blk.27.ffn_down.weight x ffn_gate_par-27 -> ffn_out-27 : 8192:3072 x 8192:1 -> 3072:1 : q4_0 x f32 -> f32 : HTP0 x HTP0 -> HTP0 : flags 0x0 ggml-hex: HTP0 get-tensor result_output : data 0x7592487000 offset 0 size 513024
-
GGML_HEXAGON_PROFILE=1Enables Op profiling (configurable via--hex-profileinrun.py):1: Basic profile with per-opusecsandcyclescounters2: Extended profile with per-opusecs,cyclesand default PMU counter data0x1,...,0x8: Extended profile with per-opusecs,cyclesand custom PMU counter data
The logging output can be saved to a file or piped directly into the post-processing script:
./scripts/snapdragon/run.py --target adb--hex-profile 1-- llama-cli ... |& \./scripts/snapdragon/ggml-hexagon-profile.py - -
GGML_HEXAGON_OPFILTER=regexFilters (disables) Ops matching the regex pattern (configurable via--hex-opfilterinrun.py):# Disable Flash Attention on Hexagon (falls back to CPU or GPU) ./scripts/snapdragon/run.py --target adb--hex-opfilter "FLASH_ATTN_EXT" -- llama-cli ...# Disable ADD and SUB on Hexagon (fall back to CPU or GPU) ./scripts/snapdragon/run.py --target adb--hex-opfilter "ADD|SUB" -- llama-cli ...
1# Snapdragon-based devices 2 3## Setup 4 5The cross-compilation toolchain images are provided by the 6[Qualcomm Snapdragon Toolchain registry](https://github.com/snapdragon-toolchain). 7These Docker images include the Android NDK, OpenCL SDK, Hexagon SDK, CMake, and the necessary cross-compilers: 8 9* **Android toolchain**: `ghcr.io/snapdragon-toolchain/arm64-android:v0.7` 10* **Linux toolchain**: `ghcr.io/snapdragon-toolchain/arm64-linux:v0.7` 11 12The unified build utility (`scripts/snapdragon/build.py`) automatically pulls 13and orchestrates these containers to perform target compilation. 14You only need to ensure that Docker (or Docker Desktop on macOS/Windows) is running on your host machine. 15Specific setup, build, and installation details for Linux and Windows on Snapdragon platforms are documented in: 16* [Linux on Snapdragon guide](linux.md) 17* [Windows on Snapdragon guide](windows.md) 18 19## How to Build 20 21### Using build.py script (Recommended) 22 23The easiest way to build llama.cpp is by using the `scripts/snapdragon/build.py` script. It automatically copies the CMake presets, 24launches the correct compilation Docker container, builds the libraries and tools, 25installs them, and optionally pushes them to your ADB device. 26 27Build and deploy for Android target (accepts `android` or `adb` alias): 28``` 29$ ./scripts/snapdragon/build.py --target adb --push 30``` 31 32Build and deploy for Linux target (accepts `linux` or `lnx` alias): 33``` 34$ ./scripts/snapdragon/build.py --target linux:user@host --push 35``` 36 37### Manual CMake Build 38 39Alternatively, you can build llama.cpp manually by entering the cross-compilation Docker container and running the CMake commands: 40 41``` bash 42# Start the cross-compilation container manually: 43~/src/llama.cpp $ docker run -it --rm -u $( id -u ) : $( id -g ) --volume $( pwd ) :/workspace --platform linux/amd64 ghcr.io/snapdragon-toolchain/arm64-android:v0.7 44 45# Inside the container, build the project using presets: 46[d]/workspace > cp docs/backend/snapdragon/CMakeUserPresets.json . 47 48[d]/workspace > cmake --preset arm64-android-snapdragon-release -B build-snapdragon 49Preset CMake variables: 50ANDROID_ABI = "arm64-v8a" 51... 52CMAKE_TOOLCHAIN_FILE = "/opt/android-ndk-r28b/build/cmake/android.toolchain.cmake" 53GGML_HEXAGON = "ON" 54GGML_OPENCL = "ON" 55GGML_OPENMP = "OFF" 56HEXAGON_SDK_ROOT = "/opt/hexagon/6.6.0.0" 57... 58-- Including OpenCL backend 59-- Including Hexagon backend 60... 61-- Build files have been written to: /workspace/build-snapdragon 62 63[d]/workspace > cmake --build build-snapdragon 64... 65[144/356] Performing build step for 'htp-v73' 66[1/16] Generating htp_iface_skel.c, htp_iface_stub.c, htp_iface.h 67[2/16] Building C object CMakeFiles/ggml-htp-v73.dir/hvx-sigmoid.c.obj 68[3/16] Building C object CMakeFiles/ggml-htp-v73.dir/htp-dma.c.obj 69[4/16] Building C object CMakeFiles/ggml-htp-v73.dir/worker-pool.c.obj 70... 71-- Installing: /workspace/build-snapdragon/ggml/src/ggml-hexagon/libggml-htp-v73.so 72-- Installing: /workspace/build-snapdragon/ggml/src/ggml-hexagon/libggml-htp-v75.so 73... 74``` 75 76To generate an installable "package" simply use cmake --install: 77 78``` 79[d]/workspace> cmake --install build-snapdragon --prefix pkg-android/llama.cpp 80-- Install configuration: "Release" 81-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-cpu.so 82-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-opencl.so 83-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-hexagon.so 84-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v73.so 85-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v75.so 86-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v79.so 87-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml-htp-v81.so 88-- Installing: /workspace/pkg-android/llama.cpp/lib/libggml.so 89... 90-- Installing: /workspace/pkg-android/llama.cpp/bin/llama-bench 91-- Installing: /workspace/pkg-android/llama.cpp/bin/llama-cli 92... 93``` 94 95## How to Install 96 97### Android 98 99For this step, your device needs to be configured for on-device development. 100Please see https://developer.android.com/studio/debug/dev-options for details. 101 102Once ADB is enabled, use `adb push` to install `pkg-android` on the device. 103**Note that the toolchain Docker image doesn't have ADB and doesn't set up the ADB bridge. Please use native ADB on the host.** 104 105``` 106~/src/llama.cpp$ adb push pkg-android/llama.cpp /data/local/tmp/ 107pkg-android/llama.cpp/bin/: 67 files pushed, 0 skipped. 190.2 MB/s (919095042 bytes in 4.607s) 108pkg-android/llama.cpp/include/: 19 files pushed, 0 skipped. 20.5 MB/s (255173 bytes in 0.012s) 109pkg-android/llama.cpp/lib/: 16 files pushed, 0 skipped. 144.4 MB/s (43801382 bytes in 0.289s) 110102 files pushed, 0 skipped. 186.9 MB/s (963151597 bytes in 4.914s) 111``` 112 113At this point, you should also install some models: 114 115``` 116~/src/llama.cpp$ wget https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_0.gguf 117... 1182025-10-11 12:04:52 (10.7 MB/s) - ‘Llama-3.2-1B-Instruct-Q4_0.gguf’ saved [773025920/773025920] 119 120~/src/llama.cpp$ adb push Llama-3.2-1B-Instruct-Q4_0.gguf /data/local/tmp/gguf 121Llama-3.2-1B-Instruct-Q4_0.gguf: 1 file pushed, 0 skipped. 38.3 MB/s (773025920 bytes in 19.250s) 122``` 123 124### Windows 125 126All artifacts are already installed in the `pkg-wos` folder. 127To run, you can use the `scripts/snapdragon/run.py` runner script (see details below). 128 129## How to Run 130 131The easiest way to run llama.cpp cli tools is using the provided `scripts/snapdragon/run.py` wrapper script. This script automatically 132maps CLI options to environment variables, resolves executable paths, and runs the command locally, via ADB, or remotely via SSH on the 133target device. 134 135llama.cpp supports three backends on Snapdragon-based devices: CPU, Adreno GPU (GPUOpenCL), and Hexagon NPU. 136You can select which backend(s) to run the model on using the `--device` option of the tool (or `--devices` option in `run.py`). 137 138Hexagon NPU behaves as a "GPU" device when it comes to `-ngl` and other offload-related options. 139 140Here are some examples of running various llama.cpp tools. 141 142Generating a completion with Gemma on Android (relying on default `HTP0:0` device and default thread count `-t 6`): 143 144``` 145~/src/llama.cpp$ ./scripts/snapdragon/run.py --target adb -- llama-completion -m models/gemma-2-2b-it-Q4_0.gguf -f prompts/sample_prompt_1024.txt --jinja -st 146... 147ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev 1 148ggml-hex: Hexagon Arch version v79 149ggml-hex: allocating new session: HTP0:0 150... 151load_tensors: offloading output layer to GPU 152load_tensors: offloaded 27/27 layers to GPU 153load_tensors: CPU model buffer size = 300.00 MiB 154load_tensors: HTP0:0 model buffer size = 1400.26 MiB 155... 156llama_perf_context_print: prompt eval time = 320.00 ms / 1024 tokens ( 0.31 ms per token, 3200.00 tokens per second) 157llama_perf_context_print: eval time = 2100.00 ms / 100 runs ( 21.00 ms per token, 47.62 tokens per second) 158``` 159 160Simple question for Llama-3.2-1B: 161 162``` 163~/src/llama.cpp$ ./scripts/snapdragon/run.py --target android --devices HTP0 -- llama-cli -m Llama-3.2-1B-Instruct-Q4_0.gguf -p "what is the most popular cookie in the world?" 164... 165ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev 1 166ggml-hex: Hexagon Arch version v79 167ggml-hex: allocating new session: HTP0 168ggml-hex: new session: HTP0 : session-id 0 domain-id 3 uri file:///libggml-htp-v79.so?htp_iface_skel_handle_invoke&_modver=1.0&_dom=cdsp&_session=0 handle 0xb4000072c7955e50 169... 170load_tensors: offloading output layer to GPU 171load_tensors: offloaded 17/17 layers to GPU 172load_tensors: CPU model buffer size = 225.49 MiB 173load_tensors: HTP0 model buffer size = 504.26 MiB 174... 175I hope this helps you understand the world's most popular cookies! [end of text] 176... 177llama_perf_sampler_print: sampling time = 30.08 ms / 487 runs ( 0.06 ms per token, 16191.77 tokens per second) 178llama_perf_context_print: load time = 617.94 ms 179llama_perf_context_print: prompt eval time = 80.76 ms / 11 tokens ( 7.34 ms per token, 136.21 tokens per second) 180llama_perf_context_print: eval time = 9210.59 ms / 475 runs ( 19.39 ms per token, 51.57 tokens per second) 181llama_perf_context_print: total time = 9454.92 ms / 486 tokens 182llama_perf_context_print: graphs reused = 473 183llama_memory_breakdown_print: | memory breakdown [MiB] | total free self model context compute unaccounted | 184llama_memory_breakdown_print: | - HTP0 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | 185llama_memory_breakdown_print: | - Host | 439 = 225 + 136 + 77 | 186``` 187 188Op test for MUL_MAT: 189 190``` 191~/src/llama.cpp$ ./scripts/snapdragon/run.py --target adb --devices HTP0:0 -- test-backend-ops -b HTP0:0 -o MUL_MAT 192... 193Backend 2/3: HTP0:0 194Device description: Hexagon 195Device memory: 2048 MB (2048 MB free) 196MUL_MAT(type_a=q4_0,type_b=f32,m=16,n=1,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],v=0,o=1): OK 197MUL_MAT(type_a=q4_0,type_b=f32,m=16,n=2,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],v=0,o=1): OK 198MUL_MAT(type_a=q4_0,type_b=f32,m=16,n=3,k=256,bs=[1,1],nr=[1,1],per=[0,1,2,3],v=0,o=1): OK 199``` 200 201Llama benchmark: 202 203``` 204~/src/llama.cpp$ ./scripts/snapdragon/run.py --target adb --devices HTP0 -- llama-bench -p 128 -n 64 -m Llama-3.2-1B-Instruct-Q4_0.gguf 205... 206ggml-hex: Hexagon backend (experimental) : allocating new registry : ndev 1 207ggml-hex: Hexagon Arch version v79 208ggml-hex: allocating new session: HTP0 209ggml-hex: new session: HTP0 : session-id 0 domain-id 3 uri file:///libggml-htp-v79.so?htp_iface_skel_handle_invoke&_modver=1.0&_dom=cdsp&_session=0 handle 0xb400007d4b231090 210| model | size | params | backend | ngl | threads | n_batch | mmap | test | t/s | 211| ---------------| ---------: | -----: | ---------- | --: | ------: | ------: | ---: | ----: | ------------: | 212| llama 1B Q4_0 | 729.75 MiB | 1.24 B | HTP | 99 | 4 | 128 | 0 | pp128 | 169.42 ± 1.75 | 213| llama 1B Q4_0 | 729.75 MiB | 1.24 B | HTP | 99 | 4 | 128 | 0 | tg64 | 51.54 ± 1.13 | 214``` 215 216## Multi-Device Execution Modes 217 218The Hexagon backend supports multiple execution and partitioning modes to accommodate different model sizes, memory 219constraints, and single- or multi-NPU hardware topologies: 220 221### 1. Single-Device Mode with Dynamic Buffer Mapping 222 223Runs the model on a single NPU session (e.g. `HTP0` or `HTP0:0`). 224 225A single NPU session provides ~3.5GB of available virtual address space. For models larger than 3.5GB, the backend 226automatically maps and unmaps weight buffers during graph execution. This allows large models to run on a single NPU 227without manual configuration: 228 229``` bash 230./scripts/snapdragon/run.py --target adb --devices HTP0:0 -- \ 231llama-cli -m models/Llama-3.2-3B-Instruct-Q4_0.gguf -ngl 99 -p "Hello" 232``` 233 234### 2. Layer-Split Mode across Virtual Sessions (`HTP0,HTP1,...` or `HTP0:0,HTP0:1,...`) 235 236Partitions model layers at load time across multiple virtual sessions hosted on a single physical NPU. 237 238Each virtual session acts as an independent backend device from llama.cpp's perspective (similar to multiple GPUs). 239Because layers are permanently distributed across sessions, each session's allocated weights remain within its private 3.5GB 240address space window, eliminating runtime buffer re-mapping overhead. 241 242Here is an example of running the GPT-OSS-20B model on a Snapdragon device using 4 virtual sessions on a single NPU: 243 244``` bash 245./scripts/snapdragon/run.py --target adb \ 246--devices HTP0:0,HTP0:1,HTP0:2,HTP0:3 -- \ 247llama-cli --load-mode none -m /data/local/tmp/gguf/gpt-oss-20b-Q4_0.gguf -t 4 \ 248--ctx-size 8192 --batch-size 128 -ctk q8_0 -ctv q8_0 -fa on -ngl 99 -no-cnv -f surfing.txt 249``` 250 251Log output snippet: 252 253``` 254... 255llama_model_loader: - type f32: 289 tensors 256llama_model_loader: - type q4_0: 96 tensors 257llama_model_loader: - type q8_0: 2 tensors 258llama_model_loader: - type mxfp4: 72 tensors 259... 260load_tensors: offloaded 25/25 layers to GPU 261load_tensors: CPU model buffer size = 1182.09 MiB 262load_tensors: HTP0:1 model buffer size = 2512.58 MiB 263load_tensors: HTP0:3 model buffer size = 2093.83 MiB 264load_tensors: HTP0:0 model buffer size = 2931.34 MiB 265load_tensors: HTP0:2 model buffer size = 2512.58 MiB 266... 267llama_perf_context_print: prompt eval time = 3843.67 ms / 197 tokens ( 19.51 ms per token, 51.25 tokens per second) 268llama_perf_context_print: eval time = 1686.13 ms / 31 runs ( 54.39 ms per token, 18.39 tokens per second) 269llama_perf_context_print: total time = 6266.30 ms / 228 tokens 270llama_memory_breakdown_print: | memory breakdown [MiB] | total free self model context compute unaccounted | 271llama_memory_breakdown_print: | - HTP0:0 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | 272llama_memory_breakdown_print: | - HTP0:1 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | 273llama_memory_breakdown_print: | - HTP0:2 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | 274llama_memory_breakdown_print: | - HTP0:3 (Hexagon) | 2048 = 2048 + ( 0 = 0 + 0 + 0) + 0 | 275llama_memory_breakdown_print: | - Host | 1476 = 1208 + 105 + 162 | 276``` 277 278### 3. Tensor-Split Mode across Physical Devices (`HTP0:0,HTP1:0,...`) 279 280Distributes model tensors across distinct physical NPU hardware cores using llama.cpp's tensor parallelism 281(`--split-mode tensor`). 282 283Tensors are partitioned across physical NPUs for parallel execution (proportions are distributed equally by default without 284needing an explicit `--tensor-split` option): 285 286``` bash 287./scripts/snapdragon/run.py --target adb \ 288--devices HTP0:0,HTP1:0 -- \ 289llama-cli -m models/Llama-3.2-3B-Instruct-Q4_0.gguf --split-mode tensor -ngl 99 -p "Hello" 290``` 291 292### 4. Row-Split Multi-Device Mode via Device Grouping (`HTP0[0-1]`) 293 294Groups multiple physical NPU cores into a single logical device using bracket notation (`HTP0[0-1]` or `HTP0[0,1]`). 295 296Unlike host-level tensor-splitting, row-splitting is executed entirely inside the Hexagon backend: 297 298``` bash 299./scripts/snapdragon/run.py --target adb \ 300--devices 'HTP0[0-1]' -- \ 301llama-cli -m models/Llama-3.2-3B-Instruct-Q4_0.gguf -ngl 99 -p "Hello" 302``` 303 304You can also combine row-splitting with layer-splitting across multiple grouped devices (e.g. `--devices 'HTP0[0-1],HTP1[2-3]'` 305on 4 physical NPUs, or `--devices 'HTP0[0-1:0],HTP1[0-1:1]'` on 2 physical NPUs using virtual sessions 0 and 1). 306 307## Environment variables 308 309- `GGML_HEXAGON_DEVICES` (default: not set, defaults to HTP0 session) 310Controls which NPU devices and sessions to allocate. Configurable via `--devices` in `run.py`: 311- `N` (single integer): Allocates `N` virtual sessions named `HTP0`, `HTP1`, ..., `HTP<N-1>` on physical NPU 0. 312- `HTP<phys>:<virt>,...`: Comma-separated list of individual devices specifying physical and virtual index: 313- `HTP0:0,HTP0:1`: Two virtual sessions on physical NPU 0 (layer-split on single NPU). 314- `HTP0:0,HTP1:0`: One session on physical NPU 0 and one on physical NPU 1 (tensor-split across physical cores). 315- `HTP<name>[<phys_spec>]`: Device grouping syntax for row-split multi-device execution: 316- `HTP0[0-1]`: A single logical device `HTP0` that groups physical cores 0 and 1. 317- `HTP0[0-1],HTP1[2-3]`: Two layer-split devices across 4 physical NPUs (cores 0-1 and 2-3). 318- `HTP0[0-1:0],HTP1[0-1:1]`: Two layer-split devices across 2 physical NPUs using virtual sessions 0 and 1. 319 320- `GGML_HEXAGON_NDEV` (deprecated) 321Replaced by `GGML_HEXAGON_DEVICES`. Controls the number of virtual sessions to allocate on physical NPU `0`. 322Allocates sessions named `HTP0`, `HTP1`, etc. 323 324- `GGML_HEXAGON_NHVX=0` 325Controls the number of HVX hardware threads to use. The default is all (actual number varies depending on the hardware version). 326 327- `GGML_HEXAGON_HOSTBUF=1` (default: 0, disabled) 328Enables allocating host buffers for debugging. By default, host buffers are disabled. 329 330- `GGML_HEXAGON_VERBOSE=1` 331Enables verbose logging of Ops from the backend. Example output: 332 333``` 334ggml-hex: HTP0 graph-compute n_nodes 2 335ggml-hex: HTP0 matmul : blk.27.ffn_up.weight x ffn_norm-27 -> ffn_up-27 : 3072:8192 x 3072:1 -> 8192:1 : q4_0 x f32 -> f32 : HTP0 x HTP0 -> HTP0 : flags 0x1 336ggml-hex: HTP0 matmul : blk.27.ffn_gate.weight x ffn_norm-27 -> ffn_gate-27 : 3072:8192 x 3072:1 -> 8192:1 : q4_0 x f32 -> f32 : HTP0 x HTP0 -> HTP0 : flags 0x3 337ggml-hex: HTP0 graph-compute n_nodes 1 338ggml-hex: HTP0 matmul : blk.27.ffn_down.weight x ffn_gate_par-27 -> ffn_out-27 : 8192:3072 x 8192:1 -> 3072:1 : q4_0 x f32 -> f32 : HTP0 x HTP0 -> HTP0 : flags 0x0 339ggml-hex: HTP0 get-tensor result_output : data 0x7592487000 offset 0 size 513024 340``` 341 342- `GGML_HEXAGON_PROFILE=1` 343Enables Op profiling (configurable via `--hex-profile` in `run.py`): 344 345- `1`: Basic profile with per-op `usecs` and `cycles` counters 346- `2`: Extended profile with per-op `usecs`, `cycles` and default PMU counter data 347- `0x1,...,0x8`: Extended profile with per-op `usecs`, `cycles` and custom PMU counter data 348 349The logging output can be saved to a file or piped directly into the post-processing script: 350 351```bash 352./scripts/snapdragon/run.py --target adb --hex-profile 1 -- llama-cli ... |& \ 353./scripts/snapdragon/ggml-hexagon-profile.py - 354``` 355 356- `GGML_HEXAGON_OPFILTER=regex` 357Filters (disables) Ops matching the regex pattern (configurable via `--hex-opfilter` in `run.py`): 358 359```bash 360# Disable Flash Attention on Hexagon (falls back to CPU or GPU) 361./scripts/snapdragon/run.py --target adb --hex-opfilter "FLASH_ATTN_EXT" -- llama-cli ... 362 363# Disable ADD and SUB on Hexagon (fall back to CPU or GPU) 364./scripts/snapdragon/run.py --target adb --hex-opfilter "ADD|SUB" -- llama-cli ... 365```