随着移动端 SoC 性能越来越强,现在已经可以直接在 Android 手机上运行 GGUF 格式的大语言模型。本文记录一次实际部署过程:使用 Termux + llama.cpp + Mesa Freedreno/Turnip + Vulkan,让高通 Adreno GPU 参与 LLM 推理。
本文以 Adreno 840 为例,并使用 LFM2.5-2.6B-Q8_0 进行测试。
一、环境
本次测试环境:
| 项目 | 配置 |
|---|---|
| 系统 | Android |
| SoC | Qualcomm Snapdragon |
| GPU | Adreno 840 |
| 推理框架 | llama.cpp |
| llama.cpp | b10516 |
| GPU Backend | Vulkan |
| Mesa | 26.0.6 |
| Vulkan 驱动 | Freedreno / Turnip |
| 模型 | LFM2.5-2.6B-Q8_0 |
| 模型大小 | 约 2.7 GB |
手机使用的是统一内存架构,因此
vulkaninfo显示的 GPU memory 并不是传统 PC 独立显卡意义上的 VRAM。
二、安装 Termux
建议使用官方 Termux 项目提供的版本,不建议使用来源不明的第三方修改版。
进入 Termux 后首先更新软件包:
pkg update
pkg upgrade
然后安装 llama.cpp:
pkg install llama-cpp
三、安装 Vulkan Backend
llama.cpp 本身只是推理框架,GPU 后端需要单独安装。
Termux 当前提供 Vulkan backend:
pkg install llama-cpp-backend-vulkan
同时安装 Vulkan 工具:
pkg install vulkan-tools
四、安装 Adreno Vulkan 驱动
对于高通 Snapdragon + Adreno GPU,可以安装 Mesa 的 Freedreno Vulkan ICD:
pkg install mesa-vulkan-icd-freedreno
这里的关键组件是:
llama.cpp
↓
Vulkan Backend
↓
Vulkan Loader
↓
Mesa Freedreno / Turnip
↓
Adreno GPU
mesa-vulkan-icd-freedreno 是让 Mesa/Turnip 为 Adreno 提供 Vulkan 支持的重要组件。
五、验证 Vulkan
安装完成后,可以使用:
vulkaninfo --summary
查看 Vulkan 是否正常工作。
也可以直接:
vulkaninfo --summary | grep -i devicename
正常情况下应该能够看到 Adreno GPU,而不是:
llvmpipe
例如本次测试环境能够识别:
Adreno (TM) 840
六、验证 llama.cpp 是否识别 GPU
仅仅 vulkaninfo 能看到 GPU 还不够,最好直接让 llama.cpp 检查。
执行:
llama-cli --list-devices
本次测试得到:
Available devices:
Vulkan0: Adreno (TM) 840 (11313 MiB, 7105 MiB free)
这意味着 llama.cpp 已经成功发现 Vulkan GPU。
至此:
Android → Termux → llama.cpp → Vulkan → Adreno 840
整个 GPU 推理链路已经打通。
七、准备 GGUF 模型
llama.cpp 使用 GGUF 格式模型。
本次使用:
LFM2.5-2.6B-Q8_0.gguf
文件大小约:
2.7 GB
对于 Adreno 840 来说,这个模型非常适合作为测试模型。
Q8_0 的优势是量化损失较小,同时模型体积又明显低于 FP16,因此非常适合移动端测试。
将模型放到 Termux 可以访问的目录,例如:
mkdir -p ~/models
然后确认:
ls -lh ~/models/
提示:如果模型在 PC 上,可以通过
scp或 Termux 内置的termux-setup-storage配合存储权限直接拉取,避免手机流量。
八、使用 Vulkan 运行模型
最简单的运行方式:
llama-cli \
-m ~/models/LFM2.5-2.6B-Q8_0.gguf \
-ngl 999
其中:
-ngl 999
表示尽可能将模型层全部 offload 到 GPU。
如果 Vulkan GPU 正常工作,启动日志中可以看到 Vulkan 设备被使用。
九、推荐配置
实际使用时,可以使用下面这组参数:
llama-cli \
-m ~/models/LFM2.5-2.6B-Q8_0.gguf \
-ngl 999 \
-c 4096 \
-b 512 \
-ub 512 \
--cache-type-k q8_0 \
--cache-type-v q8_0 \
--threads 6 \
--threads-batch 8 \
--temp 0.6 \
--jinja \
-fa on
下面解释几个比较重要的参数。
-ngl 999
让 llama.cpp 尽可能把模型层放到 GPU。
移动端 Vulkan 推理基本应该优先尝试:
-ngl 999
而不是手动设置一个很小的值。
-c 4096
设置上下文长度为 4096 tokens。
上下文越大,KV Cache 占用的内存越多。
如果手机内存比较紧张,可以降低到:
-c 2048
如果内存充足,则可以进一步尝试:
-c 8192
但上下文长度并不是越大越快。
-b 512
设置 batch size。
它主要影响 Prompt Processing。
较大的 batch 通常可以提高 GPU 对长 Prompt 的处理效率,但同时会增加内存需求。
-ub 512
这是 unified batch size,也就是 ubatch。
同样主要影响批处理过程。
移动设备上不建议一开始就把数值设置得特别大,256~512 是比较合理的测试范围。
十、KV Cache 量化
本次测试使用:
--cache-type-k q8_0
--cache-type-v q8_0
KV Cache 量化的主要目的不是提高模型智力,而是:
降低 KV Cache 的内存占用。
特别是在 8K、16K 甚至更长上下文时,KV Cache 会越来越大。
如果只是测试模型性能,也可以使用默认 KV Cache 设置进行对比。
十一、CPU 线程
配置:
--threads 6
--threads-batch 8
其中:
--threads主要影响生成阶段--threads-batch主要影响 Prompt Processing
移动端 CPU 通常采用大小核架构,因此线程数并不是越高越快。
例如可以实际测试:
4 threads
6 threads
8 threads
找到最适合当前 Snapdragon SoC 的配置。
十二、Jinja
本次使用:
--jinja
它用于让 llama.cpp 使用模型对应的 Jinja Chat Template。
对于支持聊天模板的模型,建议开启。
否则模型可能无法按照官方预期的方式组织 system / user / assistant 等消息。
不过它本身不会让 GPU 变快。
十三、Flash Attention
当前版本 llama.cpp 的参数格式是:
-fa on
而不是简单的 -fa。
可以使用:
-fa on
强制开启。
也可以:
-fa off
关闭。
默认:
-fa auto
十四、实际性能测试
使用:
LFM2.5-2.6B-Q8_0.gguf
在 Adreno 840 + Vulkan 环境下进行测试。
关闭 Flash Attention 时:
Prompt: 9.6 t/s
Generation: 10.2 t/s
开启 -fa on 之后:
Prompt: 34.7 t/s
Generation: 10.3 t/s
可以看到一个比较有意思的现象:
- Prompt Processing:从 9.6 t/s 提升到 34.7 t/s,提升非常明显
- Generation:基本没有变化(10.2 → 10.3 t/s)
也就是说:
Flash Attention 对本次测试的 Prompt Processing 有明显帮助,但对逐 token Generation 几乎没有提升。
这也是移动端 GPU 推理中比较值得注意的一点。
十五、为什么 Generation 只有约 10 t/s?
虽然 Adreno 840 是非常强的移动 GPU,但不能简单按照 PC 显卡的思路来理解。
LLM Generation 与 Prompt Processing 的工作特征不同。
Prompt Processing 可以进行大量并行计算:
大量 token
↓
GPU 并行计算
↓
较高吞吐
而 Generation 是:
生成一个 token
↓
读取模型权重
↓
计算
↓
生成下一个 token
↓
重复
因此 Generation 很容易受到:
- GPU 内存带宽
- Kernel 实现
- Vulkan backend 优化程度
- 量化格式
- KV Cache
- Android GPU 驱动
等因素影响。
所以:
Prompt 34.7 t/s
Generation 10.3 t/s
并不意味着 Adreno 840 的计算能力只有 10 tok/s。
这是当前这套 llama.cpp + Vulkan 软件栈下的实际推理吞吐。
十六、作为服务使用:llama-server(Open WebUI 可选)
单次的 llama-cli 适合交互式测试和跑 benchmark,但如果你想让它变成一个可以随时打开的本地 AI 服务,建议用 llama-server(llama.cpp 自带):
pkg install llama-cpp-server
启动服务:
llama-server \
-m ~/models/LFM2.5-2.6B-Q8_0.gguf \
-ngl 999 \
-c 4096 \
--jinja \
-host 127.0.0.1 \
-port 8080
启动后,llama-server 会暴露一个 OpenAI 兼容的 HTTP API(/v1/chat/completions 等),手机上的任何 OpenAI 兼容客户端都能直接调用:
curl http://127.0.0.1:8080/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"model": "LFM2.5-2.6B",
"messages": [{"role": "user", "content": "你好"}]
}'
llama.cpp 的 server 还自带一个极简的 Web 聊天界面(http://127.0.0.1:8080/),不装任何东西就能先聊起来。
可选:Open WebUI
如果你想要更完整的 Web 聊天体验,可以让 llama-server 和 Open WebUI 搭配使用:
# Termux 里跑 OpenAI 兼容的 llama-server(如上)
# 再在另一台设备/容器上跑 Open WebUI 前端
docker run -d -p 3000:8080 \
-e "OPENAI_API_BASE_URL=http://手机IP:8080/v1" \
ghcr.io/open-webui/open-webui:main
注意:Open WebUI 本身对 Termux/Android 不是原生安装,通常建议在 NAS / PC 上跑前端,通过局域网指向手机的 llama-server。手机上直接用自带的 Web 界面就够用了。
十七、总结与注意事项
到这里,一套完整的"Android 手机本地大模型"方案就落地了:
Android + Termux
↓
llama.cpp + Vulkan backend
↓
Mesa Freedreno/Turnip 驱动
↓
Adreno GPU 加速
↓
GGUF 模型(本地推理,完全离线)
值得记住的几点
- 离线可用:模型完全在本地,不需要联网,数据不出手机——这是相比云端最大的优势。
- Prompt 快、Generation 慢是常态:移动端 GPU 受内存带宽和驱动栈限制,单 token 生成速度通常远低于 PC 独显。别只看峰值。
- Flash Attention 值得开:对长 Prompt 输入有明显提升,且对生成速度几乎无损,默认建议
-fa on。 - 上下文大小按内存调:2.6B 模型 8K 上下文是可行的,但更小的模型(likely 1.5B)会更从容,适合跑在手机上长期待机。
- 功耗与发热:持续 Generation 会让手机发热、掉电快,长任务建议接电源并注意散热。
- 版本差异:llama.cpp 迭代很快,参数格式(如
-fa on)在不同版本间会有变化,以llama-cli --help和你安装的版本文档为准。
写在最后
在 Adreno 840 + Vulkan 这套软件栈下,LFM2.5-2.6B-Q8_0 能跑到 Prompt 34.7 t/s、Generation 10.3 t/s,已经足够支持日常的对话、翻译、简单编码辅助等轻量场景。如果只是想测试手机 GPU 的推理能力,或者需要一个完全离线的本地助手,这套方案非常值得一试。