在 WSL 里把 llama.cpp CUDA 版编译起来:一次真实的本地部署记录
原创 · 约 24 分钟阅读 · 阅读 --

在 WSL 里把 llama.cpp CUDA 版编译起来:一次真实的本地部署记录

作者: Alex Xiang


前两篇文章里,llama.cpp 一直只跑了 CPU 基线。这个结论其实很不完整:CPU 能跑,只能说明模型格式和程序链路没问题;真正要和 Ollama 做公平比较,必须把 CUDA 版也编译出来。

这篇记录一次完整的部署过程。环境是 Windows 宿主机 + WSL Ubuntu 26.04 LTS,显卡是 RTX 4060 Laptop GPU 8GB。目标很明确:不改系统目录,不依赖全局 CUDA Toolkit,用一个独立的用户态环境把 llama.cpp CUDA 后端编译起来,然后用同一份 Qwen3 4B GGUF 做基准测试。

先看最后结果

最终编译出的二进制在:

~/opt/llama.cpp/build-cuda/bin/

关键程序包括:

llama-cli
llama-server
llama-bench
libggml-cuda.so

llama-bench 能识别 CUDA 设备:

ggml_cuda_init: found 1 CUDA devices (Total VRAM: 8187 MiB):
  Device 0: NVIDIA GeForce RTX 4060 Laptop GPU, compute capability 8.9, VMM: yes, VRAM: 8187 MiB

用 Qwen3 4B 的同一份 GGUF 跑 benchmark,结果是:

后端Prompt 处理生成
CPU 版 llama.cpp约 4.5 tok/s约 12.4 tok/s
CUDA 版 llama.cpp约 1653.6 tok/s约 78.7 tok/s

对这台机器来说,CUDA 版不是“略快一点”,而是把 llama.cpp 从兼容性验证工具变成了真正可用的本地推理后端。

为什么不用系统 CUDA Toolkit

一开始的错误很直接:

Unable to find cudart library.
CUDA Toolkit not found

WSL 里能看到显卡,Ollama 也能用 GPU,这说明驱动链路是通的。但 llama.cpp 要编译 CUDA 后端,需要的是开发工具链:nvcclibcudart、cuBLAS、CUDA 头文件,以及一个 CUDA 支持的 host compiler。

Ubuntu 26.04 源里有 nvidia-cuda-toolkit 12.4,但这条路会改系统环境,也需要 root 权限。为了把实验控制在当前用户目录,我改用 micromamba 建一个独立环境:

~/.local/envs/llama-cuda

这样做有几个好处:

  • 不污染系统默认编译器。
  • 不影响已经能跑的 Ollama。
  • CUDA、GCC、cuBLAS 的版本都能固定。
  • 后面要重来,删掉这个目录就行。

安装 micromamba

先把 micromamba 放到当前用户目录:

mkdir -p ~/.local/bin ~/.local/share/micromamba ~/.cache/micromamba

curl -L https://micro.mamba.pm/api/micromamba/linux-64/latest \
  -o /tmp/micromamba.tar.bz2

tar -xjf /tmp/micromamba.tar.bz2 -C /tmp bin/micromamba
install -m 0755 /tmp/bin/micromamba ~/.local/bin/micromamba

~/.local/bin/micromamba --version

这次安装到的是:

2.8.1

建 CUDA 编译环境

第一次我尝试一次性安装 CUDA、GCC、CMake、Ninja 和相关依赖,解析没问题,但下载和事务时间太长。后来改成更小的组合,先把 CUDA 必需组件装起来:

MAMBA_ROOT_PREFIX=$HOME/.local/share/micromamba \
~/.local/bin/micromamba create -y \
  -p $HOME/.local/envs/llama-cuda \
  -c nvidia \
  cuda-nvcc=12.4 \
  cuda-cudart-dev=12.4 \
  libcublas-dev=12.4

装完后检查:

~/.local/envs/llama-cuda/bin/nvcc --version

输出里能看到:

Cuda compilation tools, release 12.4, V12.4.131

这一步之后,nvcclibcudart.solibcublas.so 都已经在用户目录里了。但这还不够,后面编译会继续暴露几个版本问题。

第一个坑:GCC 15 太新

系统默认编译器是 GCC 15:

g++ (Ubuntu 15.2.0-16ubuntu1) 15.2.0

CUDA 12.4 不支持这么新的 GCC。即使加 --allow-unsupported-compiler,也会在解析 C++ 标准库头文件时失败。典型错误包括:

unsupported GNU version! gcc versions later than 13 are not supported

以及后续一串 C++ 头文件解析错误。

解决办法是把 GCC/G++ 13 装进同一个 micromamba 环境,并让 CMake 明确使用它:

MAMBA_ROOT_PREFIX=$HOME/.local/share/micromamba \
~/.local/bin/micromamba install -y \
  -p $HOME/.local/envs/llama-cuda \
  -c conda-forge \
  gcc_linux-64=13 \
  gxx_linux-64=13

检查版本:

~/.local/envs/llama-cuda/bin/x86_64-conda-linux-gnu-g++ --version

结果是:

x86_64-conda-linux-gnu-g++ (conda-forge gcc 13.4.0-19) 13.4.0

第二个坑:缺静态 CUDA 运行库

换成 GCC 13 后,CUDA compiler 检测继续往前走,但链接阶段又报:

cannot find -lcudadevrt
cannot find -lcudart_static

这说明还缺静态运行库。补两个小包:

MAMBA_ROOT_PREFIX=$HOME/.local/share/micromamba \
~/.local/bin/micromamba install -y \
  -p $HOME/.local/envs/llama-cuda \
  -c nvidia \
  cuda-cudart-static=12.4 \
  cuda-driver-dev=12.4

这一步很快,下载量大约 1MB。

第三个坑:CUDA 组件版本混用

再往后,CUDA 源文件开始编译,但在 cuda_fp16.h 里报:

fatal error: nv/target: No such file or directory

问题出在 CUDA 组件混用了版本。环境里 cuda-nvcc 是 12.4,但 cuda-cccl 被解析到了 12.9。补齐并固定 12.4 的 CCCL 后,nv/target 头文件就出现了:

MAMBA_ROOT_PREFIX=$HOME/.local/share/micromamba \
~/.local/bin/micromamba install -y \
  -p $HOME/.local/envs/llama-cuda \
  -c nvidia \
  cuda-cccl=12.4

确认:

find ~/.local/envs/llama-cuda/include -path '*nv/target*' -print

输出:

/home/alex/.local/envs/llama-cuda/include/nv/target

这一步之后,CUDA 版 llama.cpp 才真正具备了完整编译条件。

配置 llama.cpp

配置命令如下。这里故意使用干净的 PATH,避免 WSL 中混入太多宿主机路径导致 CMake 检测变慢。

CUDA_ENV=$HOME/.local/envs/llama-cuda

rm -rf ~/opt/llama.cpp/build-cuda

PATH="$CUDA_ENV/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
LD_LIBRARY_PATH="$CUDA_ENV/lib:${LD_LIBRARY_PATH:-}" \
cmake -S ~/opt/llama.cpp -B ~/opt/llama.cpp/build-cuda \
  -DCMAKE_BUILD_TYPE=Release \
  -DGGML_CUDA=ON \
  -DCUDAToolkit_ROOT="$CUDA_ENV" \
  -DCMAKE_C_COMPILER="$CUDA_ENV/bin/x86_64-conda-linux-gnu-gcc" \
  -DCMAKE_CXX_COMPILER="$CUDA_ENV/bin/x86_64-conda-linux-gnu-g++" \
  -DCMAKE_CUDA_HOST_COMPILER="$CUDA_ENV/bin/x86_64-conda-linux-gnu-g++" \
  -DLLAMA_BUILD_SERVER=ON \
  -DLLAMA_BUILD_EXAMPLES=ON \
  -DLLAMA_BUILD_TESTS=OFF

成功配置时,关键输出是:

-- The CUDA compiler identification is NVIDIA 12.4.131 with host compiler GNU 13.4.0
-- Using CMAKE_CUDA_ARCHITECTURES=89-real CMAKE_CUDA_ARCHITECTURES_NATIVE=89-real
-- CUDA host compiler is GNU 13.4.0
-- Including CUDA backend
-- Configuring done
-- Build files have been written to: /home/alex/opt/llama.cpp/build-cuda

89-real 对应 RTX 4060 Laptop GPU 的 Ada 架构,这说明 CMake 已经识别到正确的目标架构。

编译关键目标

我只编译这三个常用目标:

CUDA_ENV=$HOME/.local/envs/llama-cuda

PATH="$CUDA_ENV/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
LD_LIBRARY_PATH="$CUDA_ENV/lib:${LD_LIBRARY_PATH:-}" \
cmake --build ~/opt/llama.cpp/build-cuda \
  --config Release \
  -j 8 \
  --target llama-cli llama-server llama-bench

编译时最慢的是 CUDA 模板实例,尤其是 ggml-cuda 里的 attention 和量化矩阵乘法相关文件。成功时能看到:

[ 41%] Linking CUDA shared library ../../../bin/libggml-cuda.so
[ 41%] Built target ggml-cuda
[100%] Built target llama-cli
[100%] Built target llama-server
[100%] Built target llama-bench

环境和构建目录最终大小:

1.7G  ~/.local/envs/llama-cuda
1.1G  ~/.local/share/micromamba/pkgs
1.1G  ~/opt/llama.cpp/build-cuda
824M  ~/opt/llama.cpp/build-cpu

如果磁盘紧张,可以在确认环境稳定后清理 micromamba 的包缓存;但我这次先保留了,方便后续补包或重建。

写一个 wrapper

CUDA 版二进制运行时需要找到 libggml-cuda.solibcudart.solibcublas.so 等库。为了不每次手写环境变量,我加了一个 wrapper:

cat > ~/.local/bin/llama-cuda-run <<'EOF'
#!/usr/bin/env bash
set -euo pipefail

CUDA_ENV="${CUDA_ENV:-$HOME/.local/envs/llama-cuda}"
LLAMA_CUDA_BIN="${LLAMA_CUDA_BIN:-$HOME/opt/llama.cpp/build-cuda/bin}"

export PATH="$CUDA_ENV/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
export LD_LIBRARY_PATH="$LLAMA_CUDA_BIN:$CUDA_ENV/lib:${LD_LIBRARY_PATH:-}"

exec "$@"
EOF

chmod +x ~/.local/bin/llama-cuda-run

以后就可以这样跑:

llama-cuda-run ~/opt/llama.cpp/build-cuda/bin/llama-bench --list-devices

输出:

ggml_cuda_init: found 1 CUDA devices (Total VRAM: 8187 MiB):
  Device 0: NVIDIA GeForce RTX 4060 Laptop GPU, compute capability 8.9, VMM: yes, VRAM: 8187 MiB
Available devices:
  CUDA0: NVIDIA GeForce RTX 4060 Laptop GPU (8187 MiB, 7099 MiB free)

跑 benchmark

模型直接使用 Ollama 已经下载好的 Qwen3 4B GGUF blob:

MODEL=$HOME/.ollama/models/blobs/sha256-3e4cb14174460404e7a233e531675303b2fbf7749c02f91864fe311ab6344e4f

llama-cuda-run ~/opt/llama.cpp/build-cuda/bin/llama-bench \
  -m "$MODEL" \
  -ngl 999 \
  -p 32 \
  -n 32

关键输出:

ggml_cuda_init: found 1 CUDA devices (Total VRAM: 8187 MiB):
  Device 0: NVIDIA GeForce RTX 4060 Laptop GPU, compute capability 8.9, VMM: yes, VRAM: 8187 MiB

| model                  | size     | params | backend | ngl | test | t/s              |
| ---------------------- | -------: | -----: | ------- | --: | ---: | ---------------: |
| qwen3 4B Q4_K - Medium | 2.32 GiB | 4.02 B | CUDA    | 999 | pp32 | 1653.55 ± 323.14 |
| qwen3 4B Q4_K - Medium | 2.32 GiB | 4.02 B | CUDA    | 999 | tg32 | 78.67 ± 0.43     |

之前 CPU 版的结果是:

pp32  约 4.5 tok/s
tg32  约 12.4 tok/s

这里的提升很明显。Prompt 处理从个位数跳到千级,生成速度也从 12 tok/s 左右提升到接近 79 tok/s。对本地交互来说,后者已经是“可以日常使用”的速度。

和 Ollama 怎么分工

CUDA 版 llama.cpp 跑通后,我不会马上替换 Ollama。两者更适合分工。

Ollama 适合日常使用:安装模型、启动服务、调用 API 都更省心,尤其是多模态模型和模板处理更方便。

llama.cpp 适合基准测试和精细控制:同一份 GGUF、同一组参数,可以更直接地观察 GPU offload、上下文、batch、采样参数对速度和显存的影响。

下一步我会用 llama-server 跑 OpenAI-compatible API,再和 Ollama 做同 prompt 的端到端延迟对比。现在至少可以确认一件事:这台 WSL 开发机上,llama.cpp CUDA 路线已经打通。