从「GPU 永远搞不定」到「GPU 好像也就这样」再到「原来 GPU 真能打」的三集连续剧
一、前情提要
上次用 POCO F2 Pro(Adreno 650)折腾了三天 GPU 加速,结局是:
- Turnip 驱动不支持 8-bit 直读 → 慢
- MIUI 原厂驱动刷进去 boot loop → 崩
- 最后老老实实 CPU dotprod 优化 → 13 t/s
结论写得很笃定:不要高估手机 GPU 的能力。
话是没错,但心里其实一直没死心——Adreno 650 实在太老了,换个有 Mali GPU 的机子会不会不一样?
于是把目光转向了自己的主力机。
二、换装备
说来讽刺,主力机 vivo X100 Pro —— 没root。之前刷 LineageOS 的副机是有 root 的,结果 GPU 驱动折腾到吐。这回想「算了,没 root 不正好有原厂 GPU 驱动」,索性再试一次。
| 项目 | 值 |
|---|---|
| 手机 | vivo X100 Pro |
| SoC | 联发科 Dimensity 9300 (mt6989) |
| GPU | Mali-G720-Immortalis MC12 |
| 内存 | 16 GB |
| Root | 没有 |
| 系统 | 原厂 OriginOS |
对比一下两台的 root 和 GPU 状态,挺有意思的:
旧副机 POCO F2 Pro(LineageOS)
├── root ✅
├── GPU Adreno 650
├── Turnip 驱动 → StorageBuffer8BitAccess 不支持 ❌
├── 原厂驱动刷入 → boot loop ❌
└── 结局 → 纯 CPU
主力机 vivo X100 Pro(原厂系统)
├── root ❌
├── GPU Mali-G720
├── 官方驱动开箱即用 ✅
└── 结局 → Vulkan 正常跑 ✅
所以说,有时候没 root 反而是好事。原厂系统的 Mali 驱动是芯片厂写好的、出厂测试过的,Vulkan 功能完整。LineageOS 上虽然你有 root 权限,但 GPU 驱动这条路反而走不通。
三、这次不用折腾驱动了——等等,还是折腾了一下
上次最大的坑是刷了 LineageOS,只能用 Turnip 开源驱动,功能残缺。这次是原厂系统,Mali 驱动是手机出厂自带的,直接 vulkaninfo 就能看到:
apiVersion = 1.3.247
driverVersion = 44.1.0
deviceName = Mali-G720-Immortalis MC12
Vulkan 1.3.247,驱动版本 44.1.0——官方驱动,功能完整,没有 StorageBuffer8BitAccess 这种坑。
但问题来了:Termux 默认安装的是 vulkan-loader-generic 包,它会附带 mesa-vulkan-icd-swrast(llvmpipe 软件渲染器),而这个包和系统 Mali 的 vulkan-loader-android 互斥冲突。结果就是 Termux 里的 Vulkan 只有 llvmpipe,根本找不到 Mali 硬件:
$ vulkaninfo
physicalDevices: count = 1
llvmpipe (LLVM 21.1.8, 128 bits) (ID: 0) ← 只有软件渲染器
更坑的是,如果你在 Termux 里执行了 pkg upgrade,它可能会悄悄把 vulkan-loader-android 换成 vulkan-loader-generic——然后一夜回到解放前。
解决办法倒简单:
# 卸掉 generic,装回 android 版
apt-get remove vulkan-loader-generic -y
apt-get install vulkan-loader-android -y
装完后 vulkan-loader-android 会自动创建一个符号链接,让 Termux 的 libvulkan.so 指向系统的 Mali 原生加载器:
Symlink /system/lib64/libvulkan.so to /data/data/com.termux/files/usr/lib/libvulkan.so ...
再看:
$ vulkaninfo
deviceName = Mali-G720-Immortalis MC12
driverVersion = 44.1.0
apiVersion = 1.3.247
Mali 回来了。 如果你也在 Termux 上遇到「Vulkan 找不到硬件 GPU」的问题,先检查装的是 vulkan-loader-generic 还是 vulkan-loader-android。
四、编译 Vulkan 版 llama.cpp
上次的那篇笔记里我写了「编译带 Vulkan 支持的 llama.cpp」,这回终于能用上了。
在 Termux 里从源码编译,开启 Vulkan 后端:
git clone https://github.com/ggerganov/llama.cpp.git
cd llama.cpp
cmake -B build -DGGML_VULKAN=ON
cmake --build build -j4
编译耗时约 50 分钟,生成了 80+ 个可执行文件。最关键的是:
libggml-vulkan.so 47 MB ✅ Vulkan 后端
libggml-cpu.so 1.1 MB ✅ CPU 后端(fallback)
libllama.so 3.4 MB ✅ llama 核心库
llama-server 6 KB ✅ 服务端(入口,动态链接)
从下午 1:32 编译到 2:22,中间还打断了一次,实际编译时间大概 40-50 分钟。手机 8 核全开编译,也是个体力活。
五、基准测试——这次是真的 GPU
驱动修好之后,用 llama-bench 重新跑了完整测试。这次测了两个模型:
- Qwen3.5-2B-Q4_K_M(1.2 GB)——跟上次 K30 Pro 同款,公平对比
- Qwen2.5-3B-Q4_K_M(2.0 GB)——想着16GB RAM,验证 GPU 效果
2B 模型:ngl=0 vs ngl=32
| ngl | Prompt 处理 | 文本生成 | 说明 |
|---|---|---|---|
| 0 | 33.38 t/s | 4.54 t/s | CPU 模式(0 层在 GPU) |
| 32 | 43.89 t/s | 17.64 t/s | GPU 全量加速 🚀 |
| 提升 | +31% | +289% | 文本生成快了近 3 倍 |
3B 模型:ngl=0 vs ngl=32
| ngl | Prompt 处理 | 文本生成 | 说明 |
|---|---|---|---|
| 0 | 16.04 t/s | 5.36 t/s | CPU 模式 |
| 32 | 24.05 t/s | 10.29 t/s | GPU 全量加速 🚀 |
| 提升 | +50% | +92% | 几乎翻倍 |
两个模型的共同规律:GPU 加速对文本生成(text generation)的效果远大于 Prompt 处理。因为 Prompt 处理本质上是大量并行计算(一次性算完所有 token),GPU 的并行优势没完全显出来;而文本生成是一个个 token 串行跑,每个 token 的计算量不大但访存密集,GPU 的带宽在这里反而成了瓶颈——所以 2B 小模型提升最大(3 倍),3B 大模型提升「只有」翻倍。
llama-bench 也确认了 GPU 正常工作:
ggml_vulkan: Found 1 Vulkan devices:
ggml_vulkan: 0 = Mali-G720-Immortalis MC12
| uma: 1 | fp16: 1 | bf16: 0 | warp size: 16
| shared memory: 32768 | int dot: 1 | matrix cores: none
六、两代手机同款模型对比
上次旧手机跑的是 Qwen3.5-2B,这次新手机也测了同款,可以对比一下:
| 项目 | POCO F2 Pro (LineageOS) | vivo X100 Pro (原厂系统) |
|---|---|---|
| SoC | Dimensity 9300 | |
| GPU | Adreno 650 | Mali-G720-Immortalis MC12 |
| 内存 | 8 GB | 16 GB |
| 模型 | Qwen3.5-2B-Q4_K_M | Qwen3.5-2B-Q4_K_M |
| 加速方式 | CPU dotprod 优化 | Vulkan GPU (ngl=32) |
| Prompt 处理 | 26.28 t/s | 43.89 t/s 🏆 快 67% |
| 文本生成 | 13.23 t/s | 17.64 t/s 🏆 快 33% |
| 首 token 延迟 | 38 ms | 23 ms 快 40% |
| 每 token 耗时 | 76 ms | 57 ms 快 25% |
| GPU 状况 | Turnip 功能残缺 ❌ | 官方驱动齐全 ✅ |
| 上次写错了手机SoC型号,K30 Pro应该是骁龙865。 |
不过也要承认:文本生成只快了 33%,说明 K30 Pro 的 CPU dotprod 优化真的做到了极致——ARM 的 SDOT 指令在 2B 小模型上已经非常接近 Mali GPU 的带宽极限了。
七、实际使用体验
服务器开起来:
llama-server -m ~/models/qwen2.5-3b-instruct-q4_k_m.gguf \
--host 0.0.0.0 --port 8080 \
-c 4096 -t 4 -ngl 32 --api-key kk
实际对话中的速度:
| 场景 | 速度 | 感受 |
|---|---|---|
| "你好" | ~3-5 tok/s | 比上次快了,但还是有加载开销 |
| "写一篇300字散文" | ~10 tok/s | ✅ 流畅阅读 |
| 连续对话(长上下文) | 9-10 tok/s | ✅ 稳定不掉速 |
比昨天体验好了不少——9-10 tok/s 的生成速度,在手机上读起来已经完全没有卡顿感了。每分钟大约 500-600 个汉字,流畅阅读完全没问题。
八、关于 Vulkan 驱动的一个小插曲
这次最让我意外的是:GPU 驱动好不容易装好了,一次 pkg upgrade 就把它打回了原形。
第二天重新测试时,发现所有 benchmark 结果都显示 ngl 没有影响。排查半天才发现 Termux 的 Vulkan 加载器从 vulkan-loader-android 换成了 vulkan-loader-generic,后者不认识 Mali 驱动。
深入看了一下 Android 的 Vulkan 架构:
应用层(llama.cpp)
↓ Vulkan API 调用
Termux 的 libvulkan.so
↓ dlopen
vulkan-loader 扫描 ICD JSON / 检测 HAL 驱动
↓
Mali 驱动发现机制:
vulkan-loader-android(系统 HAL 方式)
→ 加载 /vendor/lib64/hw/vulkan.mali.so(薄包装,10KB)
→ 该包装 dlopen /vendor/lib64/egl/libGLES_mali.so(45MB,真驱动)
vulkan-loader-generic(标准 ICD 方式)
→ 扫描 /etc/vulkan/icd.d/*.json
→ 只找到了 llvmpipe 的 JSON
→ Mali GPU 不可见 ❌
根源是 Android 的 linker namespace 隔离——Termux(作为普通 app)的默认命名空间无法加载 /vendor/lib64/ 里的 HAL 驱动库。vulkan-loader-android 包通过符号链接方式绕过了这个限制,而 vulkan-loader-generic 没有这个能力。
好在修复很简单:
pkg remove vulkan-loader-generic
pkg install vulkan-loader-android
如果你也在 Termux 上遇到类似问题,先看看 dpkg -l | grep vulkan-loader,确保装的是 android 版而不是 generic 版。
九、更新后的感悟
1. GPU 加速确实有用,但要看模型大小
2B 模型:GPU 提升 3 倍(4.5 → 17.6 t/s),效果显著 3B 模型:GPU 提升翻倍(5.4 → 10.3 t/s),还不错 推测 1B 以下模型:GPU 提升会更夸张,可能 4-5 倍
核心原因:模型越小,带宽瓶颈越不明显,GPU 的并行算力越能发挥。
2. Mali 官方驱动是真的好用
和上次 Adreno+Turnip 的惨烈经历相比,Mali 官方驱动只要装对包就稳定运行。没有奇怪的 SPIR-V 限制、没有段错误、没有 boot loop。
3. 手机上跑 LLM 的甜区可能是 1.5B-3B
3B 模型在 16GB 内存 + Mali-G720 上能稳定 10 t/s,足够阅读。2B 模型能到 17 t/s,接近桌面体验。再小(0.6B)可能太快但智商不够,再大(7B+)内存装不下。1.5-3B 可能就是手机本地推理的甜区。
十、后续还想试的
- 下载 Qwen3.5-2B 同款模型做公平对比 ✅
- 真正修好 Vulkan 驱动 ✅
- 比较 2B/3B 在 CPU 和 Vulkan 上的完整差异 ✅
- 同样是 K30 Pro,原厂 MIUI 系统的 Adreno 650 能不能用官方驱动跑 GPU 推理?──且看下回分解
2026-07-07 · vivo X100 Pro / Dimensity 9300 / Mali-G720-Immortalis MC12 / 16GB RAM
上一次折腾记录见:手机跑大模型GPU加速折腾记.md