首页
/ llama.cpp 骁龙设备后端实战:用 Hexagon NPU、Adreno OpenCL 与 CPU 在 Snapdragon 上跑 LLM 推理

llama.cpp 骁龙设备后端实战:用 Hexagon NPU、Adreno OpenCL 与 CPU 在 Snapdragon 上跑 LLM 推理

2026-09-06 14:44:35作者:齐添朝

本篇技术指南基于 llama.cpp 仓库中 docs/backend/snapdragon/README.md 及其配套的 Linux 指南Windows 指南开发细节文档 编写,系统讲解如何在 Snapdragon(骁龙)平台上完成 llama.cpp 的交叉编译、部署与推理运行。读完本文,你将掌握三件事:如何用 Docker 交叉编译工具链一键构建面向 Android/Linux on Snapdragon 的 llama.cpp 包并推送到设备;如何用统一运行脚本 run.py 在本地、ADB、SSH 三种方式下选择 CPU / Adreno GPU / Hexagon NPU 后端执行模型;以及 Hexagon NPU 后端的全部关键环境变量(设备会话、OP 过滤、性能剖析等)的含义与用法。

一、总体架构:三种后端与两条工具链

llama.cpp 在 Snapdragon 设备上支持三种推理后端:

  1. CPU:ARMv8 架构的 NEON/SVE 路径(通过 CMake 预设中的 -march=armv8.7a+fp16+dotprod+i8mm 等标志启用 SIMD 特性);
  2. Adreno GPU:通过 GPU OpenCL 路径(GGML_OPENCL=ON);
  3. Hexagon NPU:即 HTP(Hexagon Tensor Processor)后端(GGML_HEXAGON=ON)。

从源码结构看,Hexagon 后端由两部分组成(参见 developer.md):

  • libggml-hexagon:CPU 侧的 GGML 后端库;
  • libggml-htp-vNN:运行在 NPU 侧的共享库,包含 Op 分发器与内核。构建产物中会同时包含 v73、v75、v79、v81 等多个架构版本的 HTP 库,运行时根据实际硬件版本自动选择。

编译依赖两条由 Snapdragon Toolchain 提供的 Docker 镜像,镜像内已集成 Android NDK、OpenCL SDK、Hexagon SDK、CMake 及所需交叉编译器:

目标 工具链镜像
Android(arm64) ghcr.io/snapdragon-toolchain/arm64-android:v0.7
Linux on Snapdragon(arm64) ghcr.io/snapdragon-toolchain/arm64-linux:v0.7

统一构建工具是 build.py:它会自动拉取并编排上述容器完成目标编译。你只需要确保宿主机上的 Docker(或 macOS/Windows 上的 Docker Desktop)正在运行。

二、CMake 预设:三个平台、三组配置

预设文件位于 CMakeUserPresets.json,定义了 arm64-android-snapdragonarm64-windows-snapdragonarm64-linux-snapdragon 三组核心配置,各自再派生出 -debug-release 两个变体(即 arm64-android-snapdragon-release 等)。三个预设的共同点与差异值得注意:

共同点(以 Android 预设为例):

  • 编译标志:-march=armv8.7a+fp16+dotprod+i8mm -fvectorize -ffp-model=fast -fno-finite-math-only -flto -D_GNU_SOURCE(Windows 预设中去掉了 -ffp-model=fast -fno-finite-math-only,Linux 预设使用 -march=armv8.2a+fp16+dotprod);
  • GGML_OPENMP=OFFGGML_LLAMAFILE=OFFLLAMA_OPENSSL=OFF
  • GGML_HEXAGON=ON——三个平台全部启用 Hexagon NPU 后端;
  • 通过环境变量 $env{OPENCL_SDK_ROOT}$env{HEXAGON_SDK_ROOT}$env{HEXAGON_TOOLS_ROOT} 引用 SDK 路径。

关键差异

预设 工具链 OpenCL 说明
arm64-android-snapdragon $env{ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmakeANDROID_ABI=arm64-v8aANDROID_PLATFORM=android-34 ON 面向 Android 设备
arm64-windows-snapdragon 继承 base + arm64-windows-llvm(Clang/LLVM) ON 面向 Windows 11 on Snapdragon
arm64-linux-snapdragon cmake/arm64-linux-clang.cmake OFF Linux on Snapdragon 默认只启用 Hexagon 后端

build.py 在执行时会自动把该预设文件从 docs/backend/snapdragon/ 复制到仓库根目录(若版本更新会先备份旧文件为 .bak),所以手动构建时需要先执行 cp docs/backend/snapdragon/CMakeUserPresets.json .

三、构建 llama.cpp

3.1 使用 build.py(推荐方式)

build.py 会自动复制 CMake 预设、启动对应的编译 Docker 容器、构建库与工具、执行安装,并可选地把产物推送到 ADB 设备。

Android 目标(androidadb 别名):

./scripts/snapdragon/build.py --target adb --push

Linux 目标(linuxlnx 别名,需指定 SSH 地址):

./scripts/snapdragon/build.py --target linux:user@host --push

从源码可以看到 build.py 的完整参数集,比文档示例覆盖更多场景:

  • --target:编译目标与部署定义,格式为 android[:serial] / adb[:serial]linux:[user@]host / lnx: / ubuntu:、或 windows / wos(默认 android);
  • --build-dir / --install-dir:构建/安装目录名(默认 build-TARGET[-dbg]pkg-TARGET[-dbg]);
  • --jobs / -j:并行编译任务数(默认 CPU 线程数);
  • --no-docker:跳过容器,在宿主机上原生构建(Windows 目标会强制此模式,因为 Windows 构建要求原生 arm64 主机);
  • --preset:覆盖 CMake 预设;--debug:使用 -debug 预设;
  • --push:通过 ADB 或 SSH/SCP 推送产物;--target-dir:设备上的目标目录(Android 默认 /data/local/tmp/llama.cpp,Linux 默认 ~/llama.cpp);
  • --toolchain-version(默认 v0.7)与 --toolchain-url(默认 ghcr.io/snapdragon-toolchain)。

推送行为有细节值得注意:对 Android 目标,脚本先 adb shell rm -rf 清掉设备上的旧包内容再 adb push;对 Linux 目标则通过 ssh 清理 + scp -r 部署。

3.2 手动 CMake 构建

也可以手动进入交叉编译容器执行 CMake 命令:

# 手动启动交叉编译容器:
~/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

# 在容器内使用预设构建项目:
[d]/workspace> cp docs/backend/snapdragon/CMakeUserPresets.json .

[d]/workspace> cmake --preset arm64-android-snapdragon-release -B build-snapdragon
Preset 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
...

构建过程会为每个 HTP 架构版本(v73/v75/v79/v81)分别生成独立的编译目标。生成可安装“包”只需执行 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
...

Linux 目标的流程完全类似,只是使用 arm64-linux 镜像与 arm64-linux-snapdragon-release 预设,最后 cmake --install build-snapdragon --prefix pkg-linuxzip -r pkg-linux.zip pkg-linux。Windows on Snapdragon 则是原生 arm64 构建,依赖 MSVC、LLVM/Clang、Hexagon SDK 6.6+、OpenCL SDK 2.3+,且需要为 HTP 库生成测试签名证书(详细步骤见 windows.md,包括 setup-sdk.py 安装 SDK、bcdedit /set TESTSIGNING ON 启用测试签名、makecert/pvk2pfx 生成个人证书、设置 HEXAGON_HTP_CERT 等环境变量后执行 cmake --preset arm64-windows-snapdragon-release)。

四、安装到设备

4.1 Android

前提是设备已开启 ADB 开发者调试。注意:工具链 Docker 镜像内没有 ADB,也没有配置 ADB 桥接,请使用宿主机上的原生 ADB

~/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)

然后推送一个模型(文档示例使用 Llama-3.2-1B):

~/src/llama.cpp$ wget https://huggingface.co/bartowski/Llama-3.2-1B-Instruct-GGUF/resolve/main/Llama-3.2-1B-Instruct-Q4_0.gguf
~/src/llama.cpp$ adb push Llama-3.2-1B-Instruct-Q4_0.gguf /data/local/tmp/gguf

4.2 Linux on Snapdragon

pkg-linux.zip 传到目标设备后解压并设置环境变量(ADSP_LIBRARY_PATH 用于定位 NPU 侧的 HTP 库):

$ unzip pkg-linux.zip
$ cd pkg-linux
$ export LD_LIBRARY_PATH=./lib
$ export ADSP_LIBRARY_PATH=./lib

4.3 Windows

所有产物已安装在 pkg-wos 文件夹中,直接通过 run.py 运行即可。

五、运行推理:run.py 统一执行器

run.py 是运行 llama.cpp CLI 工具的最简单方式。它自动完成三件事:把 CLI 选项映射为环境变量、解析可执行文件路径、在本地 / ADB / SSH 三种通道上执行命令。

Hexagon NPU 在 -ngl 等卸载(offload)选项面前表现得像一个“GPU”设备,通过工具的 --device 选项(或 run.py--devices 选项)选择后端。

在 Android 上用 Gemma 生成补全(依赖默认 HTP0:0 设备与默认线程数 -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)

Llama-3.2-1B 简单问答(显式指定 HTP0 设备):

~/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?"
...
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
...
llama_perf_context_print:        eval time =    9210.59 ms /   475 runs   (   19.39 ms per token,    51.57 tokens per second)
llama_memory_breakdown_print: |   - HTP0 (Hexagon)     |  2048 = 2048 + (   0 =     0 +       0 +       0) +           0 |
llama_memory_breakdown_print: |   - Host               |                  439 =   225 +     136 +      77                |

测试 MUL_MAT 算子(对 NPU 后端的单算子正确性验证):

~/src/llama.cpp$ ./scripts/snapdragon/run.py --target adb --hex-hostbuf 0 --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

llama-bench 基准测试

~/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
...
| 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 |

Linux on Snapdragon 上的双 NPU 张量切分示例(来自 linux.md):

$ ./scripts/snapdragon/run.py --target ubuntu:maxk@192.168.1.87 --device HTP0:0,HTP1:0 -- llama-completion -m models/gemma-2b-it-Q4_0.gguf -f prompts/sample_prompt_1024.txt --jinja -st --split-mode tensor --ctx-size 8192

它等价于在远端执行的命令:

+ ssh maxk@192.168.1.87 "cd ~/llama.cpp && ulimit -c unlimited && LD_LIBRARY_PATH=./lib ADSP_LIBRARY_PATH=./lib GGML_HEXAGON_DEVICES=HTP0:0,HTP1:0 GGML_HEXAGON_OPPOLL=1 ./bin/llama-completion -m models/gemma-2b-it-Q4_0.gguf -f prompts/sample_prompt_1024.txt --jinja -st --split-mode tensor --ctx-size 8192 -v -n 16 --device HTP0:0,HTP1:0 -ngl 99 --ubatch-size 1024 -fa on -t 6"

run.py 源码可以看到,脚本对 llama 系工具做了若干默认参数注入(若用户未显式指定):

  • llama-cli / llama-completion / llama-server:追加 -ngl 99(尽量全部层卸载)、--ubatch-size 1024-fa on(启用 Flash Attention);
  • llama-cli / llama-completion / llama-server / llama-bench:追加 -t 6
  • 若设置了 --sched-debug--verbose--profile,自动追加 -v
  • --devices 会自动拆分:含 htp 字样的条目映射为 GGML_HEXAGON_DEVICES,其余映射为 OpenCL 的 GGML_OPENCL_DEVICE

六、Hexagon 后端环境变量详解

这是本指南最实用的部分。以下变量均由 ggml-hexagon.cpp 中的 getenv 调用解析(源码中可见 GGML_HEXAGON_OPBATCHGGML_HEXAGON_OPQUEUEGGML_HEXAGON_OPFUSIONGGML_HEXAGON_OPFILTERGGML_HEXAGON_PROFILEGGML_HEXAGON_VMEMGGML_HEXAGON_MBUFGGML_HEXAGON_HOSTBUFGGML_HEXAGON_DEVICES 等解析点)。

6.1 设备与会话:GGML_HEXAGON_DEVICES

默认未设置时回退到单个 HTP0 会话。两种配置形式:

  • 单个整数 N:分配 N 个名为 HTP0HTP1、…、HTP<N-1> 的会话(行为等同于已废弃的 GGML_HEXAGON_NDEV=N);
  • 逗号分隔的 HTP<物理索引>:<虚拟索引> 列表(或旧式 HTP<idx> 格式):例如 HTP0:0,HTP0:1 在第一个物理 NPU 上创建两个虚拟会话(可用于内存限制场景);HTP0:0,HTP1:0 则在双 NPU 设备的两块物理 NPU 上各分配一个会话。

旧变量 GGML_HEXAGON_NDEV 已废弃(源码中会打印 DEPRECATED: GGML_HEXAGON_NDEV is deprecated. use GGML_HEXAGON_DEVICES instead)。

大模型提示:每个 Hexagon 会话(Process Domain)的内存映射窗口约 3.5GB。超过该大小的模型可以靠后端的动态映射/解除映射机制在单会话上运行,也可以用 llama.cpp 标准的层切分模式把模型拆到多个 Hexagon 设备或虚拟会话上——它们从卸载与切分角度看就像多块 GPU。developer.md 给出了 GPT-OSS-20B 用单 NPU 上 4 个虚拟会话(--devices HTP0:0,HTP0:1,HTP0:2,HTP0:3)运行的完整示例,各会话 model buffer 约 2.5–2.9 GiB,KV cache 与 compute buffer 也按会话分布。

6.2 计算与缓冲

变量 作用
GGML_HEXAGON_NHVX 使用的 HVX 硬件线程数;默认全部(实际数量随硬件版本变化)。设为 0 可禁用 HVX
GGML_HEXAGON_HOSTBUF 是否分配 host 缓冲。默认除 REPACK 外的缓冲全部是 host 缓冲;测试需要 REPACK 缓冲的算子(MUL_MAT、MUL_MAT_ID)时相关设置生效
GGML_HEXAGON_OPBATCH 单个 HTP 执行中最多批处理的 OP 数
GGML_HEXAGON_OPQUEUE 异步 NPU OP 队列大小
GGML_HEXAGON_OPPOLL 是否轮询 NPU opbatch 完成(run.py 默认 1
GGML_HEXAGON_OPFUSION NPU 图节点融合优化级别(0 禁用 / 1 启用)
GGML_HEXAGON_VMEM / GGML_HEXAGON_MBUF NPU VMEM / host buffer 分配上限(MB)
GGML_HEXAGON_MM_SELECT 选择 MUL_MAT / MUL_MAT_ID 内核:3=HMX、2=HVX 分块、1=HVX 平铺、0=禁用
GGML_HEXAGON_FA_SELECT 选择 Flash Attention 内核:2=HMX、1=HVX、0=禁用
GGML_HEXAGON_ARCH 覆盖目标 Hexagon NPU 架构版本(v73/v75/v79/v81 等)

6.3 调试、过滤与剖析

GGML_HEXAGON_VERBOSE=1 —— 打印后端 OP 的冗长日志:

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=1 —— 开启 OP 剖析,三级:

  • 1:基础剖析,每 OP 的 usecscycles 计数;
  • 2:扩展剖析,附加默认 PMU 计数器数据;
  • 0x10x8:扩展剖析,附加自定义 PMU 计数器数据。

日志可以落盘后处理,也可以直接管道进后处理工具生成报告:

GGML_HEXAGON_PROFILE=1 ./scripts/snapdragon/run.py --target adb -- llama-cli ... |& ./scripts/snapdragon/ggml-hexagon-profile.py -

GGML_HEXAGON_OPFILTER=regex —— 用正则表达式过滤(禁用)匹配的 OP,使其回退到 CPU 或 GPU:

GGML_HEXAGON_OPFILTER="FLASH_ATTN_EXT" ./scripts/snapdragon/run.py --target adb -- llama-cli ...
# 在 Hexagon 上禁用 Flash Attention(回退到 CPU 或 GPU)

GGML_HEXAGON_OPFILTER="ADD\|SUB" ./scripts/snapdragon/run.py --target adb -- llama-cli ...
# 在 Hexagon 上禁用 ADD 与 SUB(回退到 CPU 或 GPU)

run.py 中还有一批 --hex-* 参数是对上述环境变量的便捷映射(如 --hex-nhvx--hex-hostbuf--hex-opfilter--hex-mm-select--hex-fa-select--hex-profile 等),此外还有 --cl-* 系列参数控制 OpenCL/Adreno 侧行为(--cl-platform--cl-device--cl-opfilter--cl-cache-dir--cl-fa-tune--cl-adreno-xmem--cl-adreno-large-buffer 等),二者互不干扰,便于做“NPU 为主、GPU 兜底”的混合部署调优。

七、小结

llama.cpp 的 Snapdragon 支持形成了完整的工具闭环:

  1. 构建scripts/snapdragon/build.py 用 Docker 交叉工具链(Android NDK + OpenCL SDK + Hexagon SDK)一键产出 pkg-*/llama.cpp 包,并支持 ADB/SCP 直接部署;
  2. 运行scripts/snapdragon/run.py 统一本地 / ADB / SSH 三种执行通道,自动拆分 HTP 与 OpenCL 设备、注入 -ngl 99-fa on--ubatch-size 1024 等适配默认值;
  3. 调优GGML_HEXAGON_DEVICES 决定 NPU 会话拓扑(单会话、多虚拟会话、双 NPU 切分),GGML_HEXAGON_PROFILE / GGML_HEXAGON_VERBOSE / GGML_HEXAGON_OPFILTER 提供剖析、追踪与算子级回退能力。

文档与源码入口汇总:主指南 docs/backend/snapdragon/README.mdLinux 指南Windows 指南Hexagon 开发细节CMake 预设构建脚本运行脚本NPU 后端实现。需要注意的前提:Docker 环境用于交叉编译,Android 设备需开启 ADB 调试,Windows on Snapdragon 需要测试签名流程才能加载 HTP 库,且所有 HTP 库均为实验性(experimental)后端,实际可用性以具体芯片(v73/v75/v79/v81 架构)与驱动版本为准。

登录后查看全文
热门项目推荐
相关项目推荐