Llama.cpp compileren met CUDA voor maximale prestaties
Binnen de route van lokaal taalmodellen draaien (kiezen, installeren, gebruiken, koppelen en beheren) bevindt dit artikel zich in de fase van installatie en prestatieoptimalisatie. We kijken hier naar de stap van kant-en-klare distributies naar een handmatig gecompileerde runtime. Wie vooraf wil bepalen welke componenten geschikt zijn, kan controleren welke hardware nodig is voor lokale LLM's om te zien of een specifieke grafische kaart voldoende geheugenbandbreedte biedt. Dit artikel behandelt het bouwen van llama.cpp vanaf de broncode met Nvidia CUDA-versnelling.
Veelgebruikte toepassingen maken onder de motorkap gebruik van llama.cpp, maar leveren vaak generiek gecompileerde binaire bestanden mee om brede compatibiliteit over meerdere GPU-generaties te garanderen. Door de broncode zelf te compileren op een Linux-systeem, stemmen we de gegenereerde machinecode af op de exacte compute architecture van de aanwezige videokaart. Dit voorkomt generieke runtime-fallbacks, benut specifieke hardware-instructies en levert een stabielere tokendoorvoer op.
Voorbeeldopstelling voor deze handleiding
- GPU (voorbeeld): Nvidia GPU met Compute Capability 8.6 (zoals de RTX 3000-serie met 12 GB VRAM) of hoger. Raadpleeg de officiële Nvidia-documentatie voor de exacte compute capability van jouw kaart.
- Systeemgeheugen: Minimaal 16 GB RAM aanbevolen voor soepele modelallocatie en compilatie.
- Opslag: Vrije schijfruimte voor de broncode, build-directory en Nvidia CUDA Toolkit.
- Voorbeeldmodel: Een instruct-model van 8 miljard parameters in een 4-bit gekwantiseerd GGUF-formaat.
- Softwareomgeving: Een recente Linux-distributie met werkende Nvidia-stuurprogramma's, CMake, GCC en de CUDA Toolkit.
Waarom handmatig compileren sneller is dan voorgebakken binaries
Generieke binaire distributies van inference-engines worden gebouwd met een breed doelpubliek voor ogen. Om fouten op oudere grafische architecturen te voorkomen, worden specifieke microcode-optimalisaties vaak uitgeschakeld of via dynamische tussenschakels afgehandeld. Wanneer we tijdens de configuratie de vlag -DCMAKE_CUDA_ARCHITECTURES meegeven, compileert de NVCC-compiler direct gerichte binaire instructies voor de Streaming Multiprocessors van de betreffende videokaart.
Daarnaast maakt het zelf compileren het mogelijk om specifieke hardwareversnellers direct in de build op te nemen. Denk hierbij aan gespecialiseerde Flash Attention-kernels die de prompt processing latency bij grotere contextvensters terugdringen, of cuBLAS-koppelingen die matrixoperaties efficiënt verdelen. In situaties met omvangrijke invoerdocumenten zorgt een hardware-specifieke build voor een merkbaar kortere verwerkingstijd voordat het eerste token verschijnt.
Een bijkomend voordeel is het vermijden van overbodige abstractielagen. Wie een dedicated serveromgeving inricht, heeft vaak geen noodzaak voor achtergrondservices of grafische beheerinterfaces. Met op zichzelf staande binaire bestanden zoals llama-cli en llama-server behoud je volledige controle over geheugenbeheer, threads en laagverdeling zonder tussenkomst van externe daemons.
Vereisten en CUDA Toolkit voorbereiden op Linux
Voor een stabiele build-omgeving gaan we uit van een gangbare Linux-installatie. Meer over de basisconfiguratie van het besturingssysteem en stuurprogramma's lees je in het overzicht over lokale LLM's draaien op Linux. Controleer altijd of de officiële Nvidia-stuurprogramma's geladen zijn voordat je begint met de installatie van ontwikkelpakketten.
Controleer via de terminal of het stuurprogramma actief is en welke driverversie geladen is:
nvidia-smi
Vervolgens installeren we de vereiste ontwikkelpakketten via het pakketbeheer van de distributie: git, cmake, een C++-compiler (zoals build-essential op op Debian gebaseerde systemen) en de Nvidia CUDA Toolkit. Raadpleeg voor distributie-specifieke pakketnamen en repositories altijd de documentatie van jouw distributie en Nvidia:
sudo apt update && sudo apt install -y \
git \
build-essential \
cmake \
libcurl4-openssl-dev \
nvidia-cuda-toolkit
Controleer na de installatie of de CUDA-compiler (NVCC) correct kan worden aangeroepen:
nvcc --version
Mocht de terminal aangeven dat nvcc niet gevonden kan worden, dan moeten de bijbehorende binaire paden handmatig aan het omgevingspad worden toegevoegd (bijvoorbeeld via ~/.bashrc):
export PATH=/usr/local/cuda/bin:$PATH
export LD_LIBRARY_PATH=/usr/local/cuda/lib64:$LD_LIBRARY_PATH
De juiste Nvidia Compute Capability bepalen
Een essentiële parameter tijdens de configuratie met CMake is de Compute Capability (CC) van de GPU. Deze numerieke waarde definieert welke generatie hardware-instructies de compiler mag genereren. Door de exacte architectuur mee te geven, wordt voorkomen dat er generieke PTX-tussencode ontstaat die pas tijdens het opstarten van het model gecompileerd hoeft te worden.
| Nvidia Architectuur | Voorbeeldmodellen | Compute Capability (Vlag) |
|---|---|---|
| Pascal | GTX 1060, GTX 1080 Ti, Tesla P40 | 61 |
| Turing | RTX 2060, RTX 2080 Ti, GTX 1660, T4 | 75 |
| Ampere | RTX 3060, RTX 3080, RTX 3090, A4000 | 86 |
| Ada Lovelace | RTX 4060, RTX 4070, RTX 4080, RTX 4090 | 89 |
| Blackwell | RTX 5080, RTX 5090 | 120 |
Bij een opstelling met een Ampere-kaart kies je dus voor architectuur 86. Beschikt een systeem over meerdere generaties videokaarten (bijvoorbeeld een Turing- en een Ampere-kaart in dezelfde machine), dan kunnen meerdere architecturen gescheiden door een puntkomma worden opgegeven, zoals 75;86.
Stap-voor-stap broncode ophalen en bouwen met CMake
We halen de broncode van llama.cpp op uit de officiële GitHub-repository. Maak vervolgens een aparte build-directory aan om de gecompileerde bestanden gescheiden te houden van de bronbestanden:
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp
mkdir build
cd build
Voer daarna CMake uit met de CUDA-parameters ingeschakeld. Met de vlag -DGGML_CUDA=ON activeren we de Nvidia backend. In het onderstaande voorbeeld stellen we de architectuur in op Ampere (CC 86) en activeren we Flash Attention voor gekwantiseerde modellen:
cmake .. \
-DGGML_CUDA=ON \
-DCMAKE_CUDA_ARCHITECTURES=86 \
-DGGML_CUDA_FA_ALL_QUANTS=ON \
-DCMAKE_BUILD_TYPE=Release
Met -DGGML_CUDA_FA_ALL_QUANTS=ON wordt Flash Attention gecompileerd voor gangbare kwantisatieformaten, wat helpt om het geheugengebruik bij langere contexten te beperken. Start vervolgens de compilatie over alle beschikbare processorkernen:
cmake --build . --config Release -j$(nproc)
Na afloop van het bouwproces staan de binaire bestanden in de map bin/. De meest gebruikte executables zijn llama-cli voor interactieve terminalprompts, llama-server voor het aanbieden van een HTTP-eindpunt, en llama-bench voor het meten van rekenprestaties.
Prestatiemetingen en verificatie met llama-bench
Om vast te stellen of de gecompileerde software daadwerkelijk de CUDA-kernen benut, starten we de ingebouwde benchmark-tool met een GGUF-modelbestand. Een gedetailleerde uitleg over hoe compressievormen zoals Q4_K_M het geheugenbeslag en de precisie beïnvloeden, vind je in de toelichting over kwantisatie uitgelegd voor lokale modellen.
Voer het benchmarkprogramma uit met een testmodel om de prompt-verwerking en generatiesnelheid te evalueren:
./bin/llama-bench \
-m ../models/model.gguf \
-n 128 \
-p 512 \
-ngl 99
Met de parameter -ngl 99 (number of GPU layers) geven we aan dat alle modellagen naar het videogeheugen moeten worden gestuurd. In het terminalverslag verschijnt direct welk CUDA-apparaat is geselecteerd voor de berekeningen.
Voor een methodische analyse van de resultaten en een helder onderscheid tussen verwerkingstijd en generatietijd kun je de concepten raadplegen in het artikel over latency, doorvoer en tokens per seconde meten. Let bij de analyse op twee primaire meetwaarden:
- Prompt processing (pp): Dit geeft aan hoeveel tokens per seconde de engine verwerkt tijdens het inlezen van de invoerprompt. Deze fase is hoofdzakelijk afhankelijk van de rekenkracht van de GPU (compute-bound).
- Text generation (tg): Dit meet het aantal gegenereerde tokens per seconde bij het produceren van het antwoord. Deze fase wordt voornamelijk begrensd door de geheugenbandbreedte van de videokaart (memory-bandwidth bound).
Kritieke runtime-parameters voor de server en CLI
De uiteindelijke tokendoorvoer hangt niet alleen af van de compilatie, maar ook van de instellingen waarmee de server wordt opgestart. Voor continue inzet starten we meestal de HTTP-serverinterface met passende parameters:
./bin/llama-server \
-m ../models/model.gguf \
-c 8192 \
-ngl 99 \
--flash-attn \
--threads 8 \
--host 127.0.0.1 \
--port 8080
De belangrijkste parameters uitgelegd:
-ngl / --n-gpu-layers: Het aantal transformatielagen dat naar de GPU wordt gestuurd. Past een model niet volledig in het VRAM, dan kan een deel van de lagen worden verplaatst, terwijl de rest op de processor draait.-c / --ctx-size: De maximale contextomvang in tokens. Een grotere context vereist meer geheugenruimte voor de Key-Value (KV) cache.--flash-attn: Activeert Flash Attention, wat de geheugengroei bij grotere contextgroottes beperkt en de prompt-evaluatie versnelt.--threads: Het aantal CPU-threads dat wordt ingezet voor operaties buiten de GPU. Het is aan te raden dit gelijk te stellen aan het aantal fysieke rekenkernen van de processor.
Geheugendistributie en bottlenecks bij hybride offloading
Wanneer een model groter is dan het beschikbare videogeheugen, biedt llama.cpp de mogelijkheid om een deel van de lagen op de GPU te plaatsen en de resterende lagen toe te wijzen aan de CPU en het systeemgeheugen. Hoewel dit toestaat om zwaardere modellen op bescheiden hardware te laden, ontstaat er een aanzienlijke vertraging.
Tijdens het genereren van tokens moeten de tussenresultaten bij elke stap worden uitgewisseld over de PCIe-bus tussen het videogeheugen en het systeem-RAM. De totale doorvoersnelheid wordt daardoor beperkt door de langzaamste schakel: de bandbreedte van het systeemgeheugen en de PCIe-interface. Hierdoor ligt de uiteindelijke tokensnelheid bij een hybride configuratie beduidend lager dan wanneer een model volledig binnen het videogeheugen past.
In de praktijk levert een iets compacter gekwantiseerd model dat volledig in het VRAM past vaak een veel responsievere interactie op dan een groter model dat over CPU en GPU verdeeld moet worden.
Foutopsporing bij compilatie en runtime
Bij het bouwen en starten van gecompileerde software met GPU-ondersteuning kunnen specifieke foutmeldingen optreden. Hieronder staan oplossingen voor veelvoorkomende situaties.
1. CUDA out of memory (OOM)
Wanneer het opstarten stopt met een geheugenfout, overschrijdt de combinatie van modelparameters, contextgrootte en KV-cache de fysieke VRAM-capaciteit. Dit kan worden opgelost door het aantal offloaded lagen (-ngl) te verlagen of een kleinere contextlengte (-c) te kiezen. Controleer via nvidia-smi of andere applicaties niet onnodig videogeheugen bezet houden.
2. CMake detecteert de CUDA-compiler niet
Als CMake meldt dat CMAKE_CUDA_COMPILER niet kan worden gevonden, ontbreken de ontwikkelheaders of staat het pad naar NVCC niet in de omgevingsvariabelen. Geef in dat geval het volledige pad mee aan de CMake-configuratie:
cmake .. -DGGML_CUDA=ON \
-DCMAKE_CUDA_COMPILER=/usr/local/cuda/bin/nvcc
3. Compiler-incompatibiliteiten
Bij gebruik van zeer recente GCC-versies op een distributie kan de CUDA Toolkit een foutmelding geven over een niet-ondersteunde GNU-compilerversie. Installeer in dat geval een specifiek ondersteunde compilerversie (zoals GCC 12) en wijs CMake daarnaar via parameters:
cmake .. -DGGML_CUDA=ON \
-DCMAKE_C_COMPILER=gcc-12 \
-DCMAKE_CXX_COMPILER=g++-12 \
-DCMAKE_CUDA_ARCHITECTURES=86
Privacy en netwerkbeveiliging bij lokaal draaien
Een belangrijke reden om zelf software vanaf broncode te bouwen en lokaal te hosten, is volledige controle over data en privacy. Bij het uitvoeren van een lokale llama-server verlaten prompts, documenten en modelantwoorden het eigen systeem niet. Er worden geen diagnostische gegevens of telemetrieverzoeken naar externe clouddiensten gestuurd. Hoe je dit binnen organisatorische kaders inricht, staat beschreven in het artikel over privacyvriendelijk AI-gebruik.
Om te zorgen dat de serverinterface niet onbedoeld openstaat voor derden in het lokale netwerk, is het verstandig om de service uitsluitend te laten luisteren op het loopback-adres (--host 127.0.0.1), tenzij er een beveiligde proxy of VPN-laag voor authenticatie tussen is geplaatst.
Onderhoud en incrementeel bijwerken
De broncode van llama.cpp wordt continu doorontwikkeld met nieuwe rekenkernels en ondersteuning voor recente modelarchitecturen. Het is niet nodig om bij updates het volledige configuratieproces opnieuw te doorlopen. Het binnenhalen van de nieuwste broncode en het herhalen van het bouwcommando volstaat:
cd llama.cpp
git pull
cd build
cmake --build . --config Release -j$(nproc)
CMake compileert dankzij de incrementele structuur alleen de bestanden die daadwerkelijk gewijzigd zijn. Hierdoor blijft de build-omgeving snel up-to-date en blijven nieuwe optimalisaties direct beschikbaar voor lokale taalmodellen.


