llama.cpp 启用 Vulkan 后端:编译与测试工作记录
日期:2026-08-23
机器:Deepin(glibc 2.28 / GCC 8.3 / CMake 3.22)· AMD Cezanne 核显(RADV RENOIR)
结论先行:编译成功、GPU 可用。提示处理提速 2.2 倍,逐 token 生成略慢于 CPU。
1. 任务目标
在本机 llama.cpp(llama.cpp,build 10470 / 34af94cd9)启用 Vulkan 后端,
并与纯 CPU 做简单会话及 token 输出速度对比。
2. 初始环境评估
| 检查项 | 结果 | 判定 |
|---|---|---|
| GPU | 04:00.0 VGA AMD/ATI Cezanne,amdgpu 驱动已加载 |
✅ |
| Vulkan 运行时 | RADV RENOIR 正确枚举,API 1.2.195,UMA 8G | ✅ |
| Vulkan Loader | /usr/lib/x86_64-linux-gnu/libvulkan.so 存在 |
✅ |
| glslc | 无(仅 2019 年 glslangValidator 7.10) | ❌ |
| SPIRV-Headers | 系统 2019 版且无 CMake config 文件 | ❌ |
| CMake ≥3.25 | 本地 3.22.1 | ❌ |
| LunarG SDK 预编译包 | 官方只提供 Ubuntu 20.04+ 构建,glibc 2.28 跑不起来 | ❌ 放弃 |
关键源码确认(动手前必做):ggml-vulkan.cpp 仅在 api_version < VK_API_VERSION_1_2 时退出,
1.3 特性全部是 #if VK_HEADER_VERSION >= 235 条件编译 → 运行时只需 Vulkan 1.2,本机满足。
3. 最终方案(全部无需 sudo,GitHub 走 ghfast.top 镜像)
在 ~/vulkan-local 组装用户级工具链:
- Kitware 独立版 CMake 3.31.6(官方为老 glibc 构建,2.28 兼容)
- 源码编译 shaderc → glslc:
- 克隆 shaderc v2022.2 + 三个 third_party 依赖(depth=1)
- commit 必须按 shaderc 的
DEPS文件锁定,不能随意配 tag(见教训 §5.4) cmake -DSHADERC_SKIP_TESTS=ON … && make -j12 glslc_exe
- Vulkan-Headers(最新 v360)+ SPIRV-Headers 各自 cmake install 到
~/vulkan-local - 用新 CMake 配置 llama.cpp:
export VULKAN_SDK=/home/john/vulkan-local PATH=$VULKAN_SDK/bin:$PATH cmake -B build -DGGML_VULKAN=ON -DGGML_NATIVE=ON cmake --build build -j12 --target llama-bench llama-completion - 纯 CPU 对照版:
cmake -B build-cpu -DGGML_VULKAN=OFF
产物:
~/vulkan-local/bin/glslc(shaderc v2022.2,配套 glslang/SPIRV-Tools commit 与 DEPS 一致)llama.cpp/build/bin/libggml-vulkan.so.0.20.1(35MB,含全部预编译 shader)
4. 测试结果(qwen2.5-0.5B Q4_K_M · 8 线程 · 3 次均值)
llama-bench -m qwen2.5-0.5b-instruct-q4_k_m.gguf -t 8 -p 256 -n 128 -r 3 [-ngl 99]
| 后端 | pp256 提示处理 tok/s | tg128 逐 token 生成 tok/s |
|---|---|---|
| 纯 CPU | 322.7 ±12.1 | 63.0 ±10.8 |
| Vulkan ngl=0(零拷贝) | 578.6 ±7.0 | 58.5 ±1.3 |
| Vulkan ngl=99(全 GPU) | 716.9 ±36.1(+122%) | 51.0 ±4.0(−19%) |
设备日志:AMD RADV RENOIR | uma: 1 | fp16: 1 | int dot: 0 | matrix cores: none
解读
- 长提示(prefill,算力瓶颈型):GPU 全量 offload 快 2.2 倍 ✅
- 逐 token(decode,带宽瓶颈型):UMA 共享内存带宽无优势,叠加老 RADV 缺 int dot/coopmat,
量化矩阵走慢速 fp16 路径 → 反而略慢 ⚠️ 升级 Mesa ≥23.x 有望改善 - 会话功能验证正常:"The capital of France is" → "Paris"
可用工具:llama-bench、llama-completion。
llama-cli / llama-server 编不过(GCC8 CTAD bug,见 §5.3)。
5. 经验教训(本次踩坑全记录)
5.1 检测方法错误导致误判
早期用 vulkaninfo | grep "Device Name" 得到空输出,误判"GPU 未被 Vulkan 识别"。
实际是 grep 模式写错——vulkaninfo 全量输出里明明有 deviceName = AMD RADV RENOIR。
教训:验证"不支持"之前,先怀疑自己的检测命令。
5.2 手工伪造 CMake config 是死路
系统 spirv-headers 不带 SPIRV-HeadersConfig.cmake,曾尝试手写假 config 骗过 find_package。
即便能过配置阶段,后面 shader 编译仍需要真正的 glslc + 新版 headers,必然失败。
教训:缺依赖就补齐真依赖(或源码自建),不要伪造包元数据绕过。
5.3 GCC 8.3 的 CTAD bug 无法低成本绕过
tools/server/server-schema.* 有 30+ 处 new field_num(...) 类模板推导。
GCC8 对"类模板含默认成员初始化器(T min = std::numeric_limits<T>::lowest())"不生成推导指引;
实测显式加 deduction guide 也无效。上游本就要求新编译器。
教训:老编译器编新代码,先看上游 CI 的最低版本要求;修不动就换目标
(本次改用不依赖 server 的 llama-completion / llama-bench)。
5.4 shaderc 依赖必须按 DEPS 锁 commit
首次用 glslang tag 11.13.0 + SPIRV-Tools tag sdk-1.3.224.1 组合,编译即报
CreateAggressiveDCEPass 签名不匹配 等 API 冲突。改为 checkout shaderc DEPS
中锁定的三个精确 commit 后一次通过。
教训:多仓库联动的项目(shaderc/glslang/SPIRV-Tools)版本必须同源锁定,
单独选 tag 大概率 API 错位。
5.5 其他小坑
make glslc只编了静态库,可执行目标名是 glslc_exegrep … | head -N提前退出会给 make 发 SIGPIPE 中断构建 → 长构建日志落盘再看- zsh 里
echo ===xxx===触发=word展开报错 - 首次 GPU 推理很慢是 shader 编译预热,属正常现象,别误判为后端故障
- UMA 核显上
ngl=0时调度器仍把算子给 GPU(backend 列显示 Vulkan),
要拿真 CPU 基线必须另建一份GGML_VULKAN=OFF的构建,而不是-ngl 0 - sudo 不可用的环境:apt / 写 /usr 都不行,方案设计从一开始就要走"用户级 prefix"路线
6. 复现步骤速查
# 0) 目录
mkdir -p ~/vulkan-build && cd ~/vulkan-build
# 1) CMake 3.31.6(ghfast.top 加速,免代理)
aria2c -x8 -o cmake.tar.gz "https://ghfast.top/https://github.com/Kitware/CMake/releases/download/v3.31.6/cmake-3.31.6-linux-x86_64.tar.gz"
tar xzf cmake.tar.gz
# 2) shaderc 及依赖(commit 按 DEPS 锁定)
git clone --depth 1 --branch v2022.2 https://ghfast.top/https://github.com/google/shaderc.git
cd shaderc/third_party
git clone --depth 1 https://ghfast.top/https://github.com/KhronosGroup/glslang.git
git clone --depth 1 https://ghfast.top/https://github.com/KhronosGroup/SPIRV-Tools.git spirv-tools
git clone --depth 1 https://ghfast.top/https://github.com/KhronosGroup/SPIRV-Headers.git spirv-headers
cd .. && git fetch --depth 1 origin f771c1293dce29e1ac3557cf994169136155c81f -q # glslang
(cd third_party/glslang && git checkout -q FETCH_HEAD)
(cd third_party/spirv-tools && git fetch --depth 1 origin 3a8a961cffb7699422a05dcbafdd721226b4547d -q && git checkout -q FETCH_HEAD)
(cd third_party/spirv-headers&& git fetch --depth 1 origin 0bcc624926a25a2a273d07877fd25a6ff5ba1cfb -q && git checkout -q FETCH_HEAD)
/usr/bin/cmake -B build -DCMAKE_BUILD_TYPE=Release -DSHADERC_SKIP_TESTS=ON \
-DSHADERC_SKIP_EXAMPLES=ON -DSPIRV_SKIP_TESTS=ON -DENABLE_CTEST=OFF
make -C build -j12 glslc_exe && mkdir -p ~/vulkan-local/bin && cp build/glslc/glslc ~/vulkan-local/bin/
# 3) 两套头文件
NC=$HOME/vulkan-build/cmake-3.31.6-linux-x86_64/bin/cmake
for d in ~/vulkan-build/shaderc/third_party/spirv-headers <Vulkan-Headers克隆目录>; do
(cd $d && $NC -B b -DCMAKE_INSTALL_PREFIX=$HOME/vulkan-local >/dev/null && $NC --install b)
done
# 4) llama.cpp
export VULKAN_SDK=$HOME/vulkan-local PATH=$VULKAN_SDK/bin:$PATH
cd llama.cpp
$NC -B build -DGGML_VULKAN=ON -DGGML_NATIVE=ON
$NC --build build -j12 --target llama-bench llama-completion
# 5) 测试(LD_LIBRARY_PATH 必须)
LD_LIBRARY_PATH=build/bin ./build/bin/llama-completion -m <model.gguf> -p "hi" -n 64 --device Vulkan0

浙公网安备 33010602011771号