← 返回文章列表

换了台手机,Vulkan 终于跑起来了——但跑起来之后发现之前根本没跑对

从「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)
GPUMali-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

nglPrompt 处理文本生成说明
033.38 t/s4.54 t/sCPU 模式(0 层在 GPU)
3243.89 t/s17.64 t/sGPU 全量加速 🚀
提升+31%+289%文本生成快了近 3 倍

3B 模型:ngl=0 vs ngl=32

nglPrompt 处理文本生成说明
016.04 t/s5.36 t/sCPU 模式
3224.05 t/s10.29 t/sGPU 全量加速 🚀
提升+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 (原厂系统)
SoCSnapdragon 855Snapdragon 865 ⚠️Dimensity 9300
GPUAdreno 650Mali-G720-Immortalis MC12
内存8 GB16 GB
模型Qwen3.5-2B-Q4_K_MQwen3.5-2B-Q4_K_M
加速方式CPU dotprod 优化Vulkan GPU (ngl=32)
Prompt 处理26.28 t/s43.89 t/s 🏆 快 67%
文本生成13.23 t/s17.64 t/s 🏆 快 33%
首 token 延迟38 ms23 ms 快 40%
每 token 耗时76 ms57 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