Lab 01-03: Building with a GPU backend¶
Objective¶
Build llama.cpp with the backend of your GPU (one of four paths: CUDA, Metal, Vulkan, HIP), check that the model really ends up in GPU memory and measure the gain over the CPU. This is a lab with alternative paths: do only the one that matches your hardware; if you have no GPU, the Vulkan path also works on many iGPUs, otherwise read it and move on to lab 01-04.
Prerequisites¶
- Lab 01-02 completed (sources cloned in
~/llama-lab/src, checkoutb11046, working toolchain). - The GPU driver installed and working (
nvidia-smi,rocminfo,vulkaninfo, or an Apple Silicon Mac). - 5-10 GB of disk space for SDK and build.
Materials¶
| Path | SDK / packages | CMake flag |
|---|---|---|
| A · CUDA (NVIDIA) | CUDA Toolkit 12.x or 13.x | -DGGML_CUDA=ON |
| B · Metal (Apple Silicon) | Xcode Command Line Tools | none (ON by default) |
| C · Vulkan (any GPU) | Linux: libvulkan-dev glslc spirv-headers; Windows/macOS: LunarG Vulkan SDK |
-DGGML_VULKAN=ON |
| D · HIP (AMD) | ROCm | -DGGML_HIP=ON -DGPU_TARGETS=gfxNNNN |
| Model | ggml-org/Qwen3.5-0.8B-GGUF (785 MiB) for the check; ggml-org/gemma-3-4b-it-qat-GGUF:Q4_K_M (~2.5 GB) to feel the difference |
flowchart TD
S{Which GPU do you have?} -->|NVIDIA| A[A · CUDA]
S -->|Apple Silicon| B[B · Metal]
S -->|AMD Linux/Windows| D[D · HIP]
S -->|Intel / iGPU / AMD without ROCm / other| C[C · Vulkan]
A & B & C & D --> V["check: --list-devices<br/>model buffer size on GPU<br/>llama bench CPU vs GPU"]Steps¶
Path A · CUDA (NVIDIA)¶
nvidia-smi # driver ok, free VRAM
nvcc --version # CUDA Toolkit in the PATH
cd ~/llama-lab/src
cmake -B build-cuda -G Ninja -DGGML_CUDA=ON # (1)!
cmake --build build-cuda --config Release -j 8 # 10-30 minutes: there are many CUDA kernels
./build-cuda/bin/llama cli --list-devices
- By default it builds for the GPU present. If
nvccdoes not see it (Cannot find valid GPU for '-arch=native'), specify the compute capability:-DCMAKE_CUDA_ARCHITECTURES="86"(RTX 30x0) or"89"(RTX 40x0); for a binary for all GPUs,-DGGML_NATIVE=OFF(much longer build). Toolkit in a non-standard path:-DCMAKE_CUDA_COMPILER=/opt/cuda-12.8/bin/nvcc.
Expected output of --list-devices: CUDA0: NVIDIA GeForce RTX … (NNNN MiB, NNNN MiB free).
Path B · Metal (Apple Silicon)¶
cd ~/llama-lab/src
cmake -B build -G Ninja # Metal and Accelerate are already ON
cmake --build build --config Release -j 8
./build/bin/llama cli --list-devices # Metal: Apple M… (unified memory)
If you did lab 01-02 on a Mac, the binary you already have is this one: Metal was active. For a build
without GPU to compare: cmake -B build-cpu -DGGML_METAL=OFF.
Path C · Vulkan¶
sudo apt install libvulkan-dev glslc spirv-headers # Debian/Ubuntu; Fedora: vulkan-loader-devel glslc spirv-headers-devel
vulkaninfo --summary | grep -iE "deviceName|apiVersion"
cd ~/llama-lab/src
cmake -B build-vulkan -G Ninja -DGGML_VULKAN=ON
cmake --build build-vulkan --config Release -j 8
./build-vulkan/bin/llama cli --list-devices
Install the LunarG Vulkan SDK (check KosmicKrisp), then:
On a Mac, Vulkan goes through MoltenVK or KosmicKrisp (translation to Metal): it is only useful for experiments.Install the LunarG Vulkan SDK with the default settings and Visual Studio 2022 with C++, then from the Developer PowerShell:
cmake -B build-vulkan -DGGML_VULKAN=ON
cmake --build build-vulkan --config Release -j 8
.\build-vulkan\bin\Release\llama.exe cli --list-devices
pacman -S mingw-w64-ucrt-x86_64-{gcc,cmake,vulkan-devel,shaderc,spirv-headers}).
Expected output in the log at load time: ggml_vulkan: Found 1 Vulkan devices: followed by
ggml_vulkan: 0 = <nome GPU> | uma: 0 | fp16: 1 | warp size: 32 … (uma: 1 on shared-memory iGPUs).
Path D · HIP (AMD)¶
rocminfo | grep -m1 gfx # e.g. gfx1100 (RX 7900), gfx1030 (RX 6800)
cd ~/llama-lab/src
HIPCXX="$(hipconfig -l)/clang" HIP_PATH="$(hipconfig -R)" \
cmake -S . -B build-hip -G Ninja -DGGML_HIP=ON -DGPU_TARGETS=gfx1100 -DCMAKE_BUILD_TYPE=Release
cmake --build build-hip --config Release -j 16
./build-hip/bin/llama cli --list-devices
HSA_OVERRIDE_GFX_VERSION=10.3.0 (RDNA2) or 11.0.0
(RDNA3) in the runtime environment. Error cannot find ROCm device library: add
HIP_DEVICE_LIB_PATH=<cartella con oclc_abi_version_400.bc>.
From the x64 Native Tools Command Prompt for VS:
set PATH=%HIP_PATH%\bin;%PATH%
cmake -S . -B build-hip -G Ninja -DGPU_TARGETS=gfx1100 -DGGML_HIP=ON -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release
cmake --build build-hip
HSA_OVERRIDE_GFX_VERSION does not work on Windows: if the GPU is not supported, use Vulkan.
Checking the offload (all paths)¶
B=./build-cuda/bin # or build / build-vulkan / build-hip
$B/llama cli -hf ggml-org/Qwen3.5-0.8B-GGUF -ngl 99 -st -p "ciao" -n 1 -rea off -v 2>&1 \
| grep -E "model buffer size|KV buffer size|compute buffer size|memory breakdown|Host|CUDA0|Vulkan0|Metal|ROCm0"
Look for the lines load_tensors: CUDA0 model buffer size = NNN MiB (or Vulkan0, Metal, ROCm0) and
llama_kv_cache: CUDA0 KV buffer size: if the buffers are on CPU, the offload did not happen. The
memory breakdown table shows, for each device, how much is used by model, context and compute buffer. With -ngl 0
the weights stay on the CPU but the device can still take part in the computation; --device none turns it off completely.
CPU versus GPU benchmark¶
M=$(ls ~/.cache/huggingface/hub/models--ggml-org--Qwen3.5-0.8B-GGUF/snapshots/*/*.gguf)
$B/llama bench -m "$M" -p 512 -n 128 -ngl 0 -r 3 # CPU only
$B/llama bench -m "$M" -p 512 -n 128 -ngl 99 -r 3 # everything on GPU
Output: a table with pp512 (prompt processing, tokens/s) and tg128 (generation). Expect a factor of
5-20× on pp and 3-10× on tg depending on the GPU; with a 0.8 B model in Q8_0 the GPU is almost wasted, so repeat with
ggml-org/gemma-3-4b-it-qat-GGUF:Q4_K_M (llama download -hf … then -m) if you have ≥ 4 GB of VRAM.
Check¶
-
--list-deviceslists the device of the backend you built, with the available memory. - In the load log with
-ngl 99, themodel buffer sizeis on the GPU device, not onCPU. -
llama benchshows higherpp512andtg128with-ngl 99than with-ngl 0. - You can explain every CMake flag you used.
Questions and variations¶
- With a model larger than the VRAM, what does
-ngl 20do? And--fit on(default) without-ngl? - Why does the concept of "copying the weights into VRAM" not exist on Apple Silicon?
- Variation (NVIDIA):
GGML_CUDA_ENABLE_UNIFIED_MEMORY=1with a model too large for the VRAM: does it work? How slow is it? - Variation: build with two backends (
-DGGML_CUDA=ON -DGGML_VULKAN=ON) and choose at runtime with--device Vulkan0. With-DGGML_BACKEND_DL=ONthe backends become.sofiles loaded on the fly: list them inbuild/bin. - Variation: compare your binary with the installer's (which detected CUDA/ROCm/Vulkan on its own): same speeds?
Deliverable¶
A gpu.md file with: GPU and driver (nvidia-smi/rocminfo/vulkaninfo/system_profiler), the
CMake command used, the output of --list-devices, the model buffer size and memory breakdown lines with -ngl 99,
the llama bench CPU versus GPU table and a comment on the speedup factors.