首页
/ llama.cpp 在 Snapdragon Linux 设备上的实战:Docker 交叉编译、build.py 自动化部署与多 NPU 推理

llama.cpp 在 Snapdragon Linux 设备上的实战:Docker 交叉编译、build.py 自动化部署与多 NPU 推理

2026-09-06 14:51:19作者:滑思眉Philip

本文围绕 Snapdragon Linux 设备构建指南 展开,完整覆盖 llama.cpp 在骁龙 Linux 平台上的交叉编译、部署与运行流程。读完本篇,你将掌握:使用 ghcr.io/snapdragon-toolchain/arm64-linux:v0.7 工具链容器完成 arm64 目标编译、通过 build.py / run.py 脚本实现"一条命令构建并推送到设备、一条命令远程执行推理"的完整工作流,以及多 NPU(Hexagon HTP)张量切分推理的配置方法。

为什么需要交叉编译:Snapdragon 的后端与工具链

在 Snapdragon 平台上,llama.cpp 支持三类后端:CPU、Adreno GPU(OpenCL)和 Hexagon NPU(即 HTP 设备)。其中 Hexagon NPU 在 -ngl 等卸载参数的语义上被当作"GPU"设备处理。骁龙 Linux 设备(例如某些开发板、车载或嵌入式形态的 x86 之外的 arm64 主机)与桌面 Linux x86 主机构型不同,因此需要在开发机上交叉编译出 arm64 目标代码,并生成 NPU 端需要的骨架库(skel)。

交叉编译由 Snapdragon Linux Docker 工具链镜像完成,镜像中内置了 Hexagon SDK、交叉编译器与 CMake。Snapdragon 系列的构建文档入口见 Snapdragon 总览,Windows 平台的对应流程见 windows.md

Linux 工具链镜像为:

ghcr.io/snapdragon-toolchain/arm64-linux:v0.7

统一构建脚本 scripts/snapdragon/build.py 会自动拉取该容器并编排整个编译过程,你只需确保宿主机上 Docker 处于运行状态。

ggml/src/ggml-hexagon/CMakeLists.txt 可以看出,Hexagon 后端构建依赖 HEXAGON_SDK_ROOTHEXAGON_TOOLS_ROOT 两个路径,工具链镜像正是把这两个变量预置好的环境,这也是"手动裸机交叉编译很难走通、而容器方式开箱即用"的根本原因。

构建方式一:build.py 脚本(推荐)

scripts/snapdragon/build.py 是最省心的构建方式。它自动完成四件事:复制 CMake presets、启动正确的编译容器、构建并安装库与工具、可选地推送到目标设备。

构建并部署到 Linux 目标(--target 接受 lnxlinux 别名,后面跟 user@host):

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

build.py 关键参数

参数 说明
--target 目标与部署定义。Linux 可写 lnx:user@hostlinux:user@hostubuntu:user@host(见 build.py 的 parse_target
--push 构建完成后通过 SSH/SCP 把产物推送到设备,Linux 默认落到 ~/llama.cpp
--target-dir 指定设备端部署目录(Linux 默认 ~/llama.cpp
--build-dir 构建目录名,默认 build-<target>(如 build-lnx
--install-dir 安装目录名,默认 pkg-<target>
-j / --jobs 并行编译任务数,默认取宿主机 CPU 核数
--preset 覆盖使用的 CMake preset
--debug 使用 -debug 而非 -release preset,产物目录带 -dbg 后缀
--no-docker 不在容器中执行,而是在宿主机原生编译
--toolchain-version 工具链镜像 tag,默认 v0.7
--toolchain-url 镜像仓库地址,默认 ghcr.io/snapdragon-toolchain

脚本内部做了什么

结合 build.py 源码,脚本的实际行为是:

  1. 同步 preset 文件:把 docs/backend/snapdragon/CMakeUserPresets.json 复制到仓库根目录(若 docs 版本更新,会先备份旧文件为 .bak 再覆盖);
  2. 选择 preset:Linux 目标默认使用 arm64-linux-snapdragon-release--debug 时切换为 -debug);
  3. 启动 Docker 容器:等价于执行 docker run --rm --volume <repo>:/workspace --workdir /workspace --platform linux/amd64 [-u uid:gid] <镜像> bash -c "cmake --preset ... && cmake --build ... -j N && cmake --install ...",即把仓库挂载为 /workspace 后依次完成 configure、build、install 三步;
  4. 推送部署--push 时通过 ssh 清理远端 ~/llama.cpp 下过期文件,再用 scp -rpkg-*/llama.cpp 目录整体拷贝到设备(见 build.py 的部署段)。

arm64-linux-snapdragon preset 的配置细节

CMakeUserPresets.json 中 Linux preset 的关键缓存变量:

"cacheVariables": {
    "CMAKE_TOOLCHAIN_FILE": "cmake/arm64-linux-clang.cmake",
    "CMAKE_C_FLAGS":   "-march=armv8.2a+fp16+dotprod -fvectorize -fno-finite-math-only -flto -D_GNU_SOURCE",
    "CMAKE_PREFIX_PATH":  "$env{OPENCL_SDK_ROOT}",
    "HEXAGON_SDK_ROOT":   "$env{HEXAGON_SDK_ROOT}",
    "HEXAGON_TOOLS_ROOT": "$env{HEXAGON_TOOLS_ROOT}",
    "PREBUILT_LIB_DIR": "linux_aarch64",
    "GGML_OPENMP":      "OFF",
    "GGML_LLAMAFILE":   "OFF",
    "GGML_OPENCL":      "OFF",
    "GGML_HEXAGON":     "ON",
    "LLAMA_OPENSSL":    "OFF"
}

几个值得注意的点:

  • 交叉工具链文件为 cmake/arm64-linux-clang.cmake
  • 与 Android preset 不同,Linux preset 中 GGML_OPENCLOFFGGML_HEXAGONON——即 Snapdragon Linux 路径默认只启用 Hexagon NPU 后端(加上 CPU),不编 Adreno OpenCL 后端;
  • PREBUILT_LIB_DIR 设为 linux_aarch64,对应工具链中该平台的 Hexagon 预编译库;
  • 该 preset 标记为 hidden,对外暴露的是继承它的 arm64-linux-snapdragon-releasearm64-linux-snapdragon-debug 两个 preset。

构建方式二:手动 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-linux:v0.7

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

[d]/workspace> cmake --preset arm64-linux-snapdragon-release -B build-snapdragon

[d]/workspace> cmake --build build-snapdragon -j $(nproc)

生成可安装的"包"只需执行 cmake --install 再打包:

[d]/workspace> cmake --install build-snapdragon --prefix pkg-linux
[d]/workspace> zip -r pkg-linux.zip pkg-linux

安装阶段会输出各库的安装路径,其中 lib/libggml-hexagon.so 是宿主侧 Hexagon 后端运行时,lib/libggml-htp-v73.solibggml-htp-v75.solibggml-htp-v79.solibggml-htp-v81.so 是面向不同 Hexagon 架构版本的 NPU 端 skel 库(可对照 ggml/src/ggml-hexagon 目录结构)。运行时设备会按实际 NPU 架构选择加载对应的 v7x 库。

部署安装:传输产物、配置环境变量、下载模型

pkg-linux.zip 传到目标 Linux 设备后解压,并设置两个必需的环境变量:

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

其中 LD_LIBRARY_PATH 让动态链接器找到 lib/ 下的 ggml 系列库;ADSP_LIBRARY_PATH 是 Hexagon 平台加载 DSP 侧 skel 库(libggml-htp-vXX.so)所依赖的路径,二者缺一不可。

随后在设备上准备好模型文件,例如:

$ wget https://huggingface.co/bartowski/Llama-3.2-3B-Instruct-GGUF/resolve/main/Llama-3.2-3B-Instruct-Q4_0.gguf

运行推理:run.py 统一入口

本地运行

在 Snapdragon Linux 设备上可以直接用 scripts/snapdragon/run.py 运行:

$ ./scripts/snapdragon/run.py --devices HTP0 -- llama-cli -m Llama-3.2-3B-Instruct-Q4_0.gguf -ngl 99 -p "what is the most popular cookie in the world?"

从开发机远程运行

通过 --target 指定 SSH 目标,即可从宿主机远程驱动设备上的推理:

$ ./scripts/snapdragon/run.py --target lnx:user@host --devices HTP0 -- llama-cli -m Llama-3.2-3B-Instruct-Q4_0.gguf -ngl 99 -p "what is the most popular cookie in the world?"

run.py 对 Linux 目标的执行逻辑在 run.py 的 SSH 分支:它把 cd ~/llama.cpp && ulimit -c unlimited && LD_LIBRARY_PATH=./lib ADSP_LIBRARY_PATH=./lib <其余环境变量> <命令> 组装成一条远端 shell 命令,经 ssh 下发执行。ulimit -c unlimited 用于在 NPU 崩溃时生成 core dump 便于调试。

多 NPU 系统的张量切分示例

在多 NPU 机器上,可以指定两个 HTP 做张量切分(tensor split)的远程补全:

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

run.py 的设备解析逻辑 看,--devices/--device 参数中凡是含 htp(不区分大小写)的条目会被收集进 GGML_HEXAGON_DEVICES,其余条目则映射为 GGML_OPENCL_DEVICE,从而实现 NPU 与 GPU 的混合指定。上例最终在远端实际执行的是:

+ 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 自动注入的默认项(见 run.py 的默认参数注入):

  • -ngl 99:把尽可能多的层卸载到 NPU(用户未显式指定时注入);
  • --ubatch-size 1024:加大微批处理尺寸;
  • -fa on:默认开启 Flash Attention;
  • -t 6:默认 6 个 CPU 线程;
  • GGML_HEXAGON_OPPOLL=1--hex-oppoll 的默认值,开启 NPU opbatch 完成轮询。

直接在设备上运行二进制

不用脚本包装时,也可以直接进入包目录执行二进制(前提是 LD_LIBRARY_PATH 已设置好):

$ ./bin/llama-cli -m Llama-3.2-3B-Instruct-Q4_0.gguf --device HTP0 -ngl 99 -p "what is the most popular cookie in the world?"

run.py 中 Hexagon 相关参数的补充说明

除设备选择外,run.py 还暴露了一组 --hex-* 参数,全部映射为 GGML_HEXAGON_* 环境变量,便于在 Linux 设备上做 NPU 调优与诊断:

参数 对应环境变量 作用
--hex-verbose GGML_HEXAGON_VERBOSE 后端算子级详细日志
--hex-profile GGML_HEXAGON_PROFILE NPU 算子性能剖析
--hex-nhvx GGML_HEXAGON_NHVX HVX 单元数量
--hex-nhmx GGML_HEXAGON_NHMX HMX 单元数量,0 表示不启用 HMX
--hex-opbatch GGML_HEXAGON_OPBATCH 单次 HTP 执行打包的最大算子数
--hex-oppoll GGML_HEXAGON_OPPOLL 是否轮询 opbatch 完成,默认 1
--hex-opfilter GGML_HEXAGON_OPFILTER 正则过滤哪些算子卸载到 NPU(可让个别算子回退 CPU)
--hex-arch GGML_HEXAGON_ARCH 覆盖目标 Hexagon 架构版本(v73/v75/v79/v81 等)

更多环境变量的完整说明(含 GGML_HEXAGON_DEVICESHTP<phys>:<virt> 会话格式、GGML_HEXAGON_PROFILE 的分级剖析、opfilter 回退策略等),可参考 Snapdragon 总览文档的 Environment variables 一节

小结

针对 Snapdragon Linux 设备,llama.cpp 的完整链路是:arm64-linux:v0.7 工具链容器交叉编译(preset arm64-linux-snapdragon-release,启用 GGML_HEXAGON)→ cmake --install 生成 pkg-linux 包 → SCP/手动传输到设备并设置 LD_LIBRARY_PATHADSP_LIBRARY_PATH → 用 run.pyHTP0(或多 NPU HTP0:0,HTP1:0 张量切分)驱动 llama-cli / llama-completion 推理build.py --pushrun.py --target 两个脚本把这条链路压缩成了两条命令,是当前仓库中面向骁龙 Linux 平台最推荐的工程化实践。

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