Ollama GPU-Performance systematisch optimieren
Von 36 auf 60 tokens/s: Die komplette Diagnose und Optimierung eines Ollama-Servers mit NVIDIA RTX 4000. Zwei identische Systeme, eine 64% Performance-Differenz. Was lief schief und wie behebt man es systematisch?
Inhalt
Inhaltsverzeichnis
- Die Ausgangslage: Zwei Hetzner Server, eine Performance-Lücke
- Die systematische Diagnose-Checkliste
- Fix #1: Die OLLAMA_LLM_LIBRARY Falle
- Fix #2: Ollama Version 0.11.2 nach 0.12.6
- Fix #3: Flash Attention aktivieren
- Fix #4: Context Length richtig einstellen
- Feintuning: GPU Clock Lock und Persistence Mode
- Die komplette Performance-Matrix
- Lessons Learned: Die Checkliste für jedes Ollama-System
Die Ausgangslage
Bitte wähle die Ausführlichkeit: kurz und einfach ausführlicher sehr ausführlich
Zwei baugleiche Hetzner GEX44 Server mit NVIDIA RTX 4000 SFF Ada (20GB VRAM), Debian 13, Ollama für lokale LLM-Inferenz. Beide mit dem gleichen Modell: gpt-oss:20b (20.9 Milliarden Parameter, MXFP4 Quantisierung).
Server A: 59.98 tokens/s bei Benchmark-Tests. Läuft sauber, GPU wird voll genutzt.
Server B: 36.54 tokens/s bei identischen Tests. Gleiche Hardware, gleiches Modell. Was lief schief?
Das sind nicht nur ein paar Prozent Differenz. Das sind 39% weniger Performance. Ich mag solche Probleme, weil sie nicht offensichtlich sind. Das System funktionierte ja, nur eben mit angezogener Handbremse.
Die Hardware-Basis
Beide Server: Hetzner GEX44 Dedicated mit identischer Ausstattung:
- GPU: NVIDIA RTX 4000 SFF Ada Generation (20GB VRAM)
- CUDA Cores: 6144
- Compute Capability: 8.9
- Treiber: NVIDIA 580.95.05
- CUDA: Version 13.0
- Betriebssystem: Debian 13 (Trixie)
Das Test-Modell
gpt-oss:20b als Benchmark-Modell:
- 20.9 Milliarden Parameter
- MXFP4 Quantisierung (mixed precision floating point 4-bit)
- 13 GB Modellgröße (passt bequem in 20GB VRAM)
- Context Window: 131.072 Tokens trainiert
Der Performance-Gap
Benchmark-Prompt: "Write a detailed explanation of quantum computing in exactly 200 words."
| System | tokens/s | Differenz |
|---|---|---|
| Server A (Referenz) | 59.98 | Baseline |
| Server B (Problem) | 36.54 | -39% |
Aus meiner Erfahrung ist das kein normales Rauschen. Das deutet auf einen fundamentalen Konfigurationsfehler hin. Aber welchen?
Die Infrastruktur im Detail: Zwei professionelle Hetzner GEX44 Dedicated Server. Ich nutze solche dedizierten Systeme gerne für produktive KI-Workloads, weil man die volle Kontrolle hat und keine Ressourcen mit anderen teilen muss.
Die Hardware-Spezifikationen
Die NVIDIA RTX 4000 SFF Ada Generation ist eine Workstation-GPU der Professional-Serie:
- VRAM: 20GB GDDR6 (ausreichend für 20B Modelle mit Puffer)
- CUDA Cores: 6144 (Ada Lovelace Architektur)
- Compute Capability: 8.9 (neueste Generation)
- Memory Bandwidth: 280 GB/s
- TDP: 70W (effizient für Dauerbetrieb)
- PCIe: Gen 4 x16
Aus meiner Perspektive eine solide Wahl für LLM-Inferenz. Nicht die schnellste Consumer-GPU (das wäre die RTX 4090), aber professionelle Hardware mit ECC-Support und stabilen Treibern.
Die Software-Umgebung
Debian 13 (Trixie) auf beiden Systemen. Bewusst aktuell gehalten, weil neuere Kernel oft bessere GPU-Unterstützung mitbringen. NVIDIA-Treiber 580.95.05 (proprietär, aber stabil in meiner Erfahrung), CUDA Toolkit 13.0 über Ollama-eigene Bibliotheken.
Ollama als LLM-Inference-Server. Das ist mein bevorzugtes Tool für lokale Modelle, weil es unkompliziert ist und gut mit NVIDIA-GPUs funktioniert (normalerweise zumindest).
Das Test-Modell: gpt-oss:20b
Für alle Benchmarks nutzte ich gpt-oss:20b. Warum dieses Modell?
- Größe: 20.9B Parameter (groß genug für aussagekräftige Tests)
- Quantisierung: MXFP4 (optimal für Ada-GPUs)
- VRAM-Nutzung: Circa 14GB bei 4k Context (passt bequem)
- Context Window: 131.072 Tokens trainiert, standardmäßig 4.096 genutzt
- Reproduzierbarkeit: Identisches Modell auf beiden Servern
Der Benchmark-Prozess
Ich nutzte einen konsistenten Benchmark-Prompt für alle Tests:
ollama run gpt-oss:20b "Write a detailed explanation of quantum computing in exactly 200 words." --verbose
Dieser Prompt generiert circa 1.500 bis 3.000 Tokens (je nach Modell-Kreativität beim "exakt 200 Wörter" zählen). Die Länge ist wichtig: Kurze Prompts (10 bis 20 Tokens) zeigen nicht die echte Durchsatz-Performance, weil die GPU nicht auf volle Taktrate hochfährt. Lange Prompts (1.500 Plus Tokens) geben ein realistisches Bild.
Das --verbose Flag ist entscheidend. Es liefert detaillierte Metriken:
- prompt eval rate: Wie schnell wird der Input-Prompt verarbeitet
- eval rate: Wie schnell werden neue Tokens generiert (das ist die Hauptmetrik)
- total duration: Gesamtzeit
- load duration: Wie lange dauert das Modell-Laden
Die Diskrepanz
Nach mehreren Testläufen auf beiden Systemen kristallisierte sich ein klares Muster heraus:
| Metrik | Server A | Server B | Delta |
|---|---|---|---|
| Token Generation | 59.98 t/s | 36.54 t/s | -39% |
| Prompt Processing | 82.75 t/s | 117.16 t/s | +42% |
| Total Time | 26.8s | 46.7s | +74% |
Interessant: Server B war beim Prompt Processing schneller (117 vs. 82 t/s), aber bei der Token-Generierung katastrophal langsamer (36 vs. 60 t/s). Das deutet auf unterschiedliche Optimierungsstufen hin. Prompt Processing nutzt andere GPU-Features als Token Generation.
Das Tückische: Beide Systeme liefen ohne Fehler. Keine Warnungen in den Logs, keine Abstürze, keine offensichtlichen Probleme. Server B "funktionierte" einfach nur mit deutlich reduzierter Leistung.
Aus meiner Erfahrung sind solche subtilen Performance-Probleme die schwierigsten. Wenn etwas abstürzt, weiß man wenigstens, wo man ansetzen muss. Wenn es langsam läuft, aber läuft, ignoriert man es oft monatelang. Nicht gut.
Die systematische Diagnose-Checkliste
kurz und einfach ausführlicher sehr ausführlich
Wenn Ollama zu langsam ist, gibt es eine klare Checkliste. Ich arbeite die immer der Reihe nach durch, vom größten Impact zum kleinsten:
- Ollama-Version prüfen: Alles unter 0.12.x ist veraltet
- Flash Attention aktiviert? Ohne geht es deutlich langsamer
- Wird die GPU genutzt? Oder läuft es auf der CPU?
- Context Length sinnvoll? Höher ist nicht immer besser
- GPU Clock im Idle? Dann fehlt Performance Mode
Diese fünf Punkte decken 95% aller Performance-Probleme ab. Der Rest ist Feintuning.
Die Checkliste im Detail
1. Ollama-Version prüfen
ollama --version
Was ist gut? Version 0.12.6 oder neuer Was ist problematisch? Alles unter 0.12.0 (deutliche Performance-Nachteile)
Real-World-Beispiel: Server B lief mit Ollama 0.11.2. Nach Upgrade auf 0.12.6: Sofortiger Sprung von 36.54 auf 55.41 tokens/s. Das sind plus 52% nur durch ein Software-Update.
2. Flash Attention Status
systemctl show -p Environment ollama.service | grep FLASH_ATTENTION
Erwartung: OLLAMA_FLASH_ATTENTION=true oder =1
Wenn nicht gesetzt: Massive Performance-Einbußen bei längeren Contexts
3. GPU-Nutzung verifizieren
# Während Ollama läuft:
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv,noheader,nounits
Erwartung: Ollama-Prozess mit 12.000 bis 14.000 MB VRAM Wenn 0 MB: CPU-Fallback! GPU wird nicht genutzt
4. Context Length prüfen
curl -s http://localhost:11434/api/ps | python3 -m json.tool | grep context_length
Sinnvolle Werte: 2048 bis 8192 Standard: 4096 (guter Kompromiss) Höher: Mehr VRAM, aber nicht schneller!
5. GPU Clock State
nvidia-smi --query-gpu=clocks.gr,clocks.mem --format=csv,noheader
Im Idle: 210 MHz Graphics, 405 MHz Memory Unter Last: 1560 MHz Graphics, 7001 MHz Memory Wenn im Idle trotz Last: GPU-Takt muss gelockt werden
Die systematische Diagnose beginnt immer beim größten Hebel und arbeitet sich zu den Details vor. Aus meiner Erfahrung spart das Zeit. Viele fangen bei Details an (GPU-Takt, Memory-Clocks) und übersehen die großen Probleme (veraltete Software, falsche Config).
Check #1: Ollama-Version (Impact: sehr hoch)
Die Ollama-Version zu prüfen klingt banal, hatte aber den größten Impact in meiner Analyse.
# Version prüfen
ollama --version
# Erwartete Ausgabe:
Warning: could not connect to a running Ollama instance
Warning: client version is 0.12.6
Warum ist die Version so wichtig? Ollama entwickelt sich rasant. Zwischen Version 0.11.2 und 0.12.6 liegen mehrere Performance-Optimierungen:
- Verbesserte CUDA-Kernel für Ada-GPUs (RTX 4000/4090 Serie)
- Optimiertes Memory-Management für große Modelle
- Flash Attention 2.0 Support (statt 1.0)
- Besseres GPU-Discovery (erkennt mehr Hardware-Konfigurationen)
In meinem Test: Server B mit 0.11.2 schaffte 36.54 t/s. Nach Upgrade auf 0.12.6: 55.41 t/s. Das sind 18.87 tokens/s mehr, also plus 52% Performance nur durch ein Software-Update. Keine Hardware-Änderung, keine aufwendige Konfiguration. Einfach curl -fsSL https://ollama.com/install.sh | sh ausführen.
Aus meiner Perspektive ist das der wichtigste Check. Bevor man Stunden mit Config-Tuning verbringt, erst die Software aktualisieren.
Check #2: Flash Attention (Impact: sehr hoch)
Flash Attention ist ein Algorithmus, der die Attention-Berechnung in Transformer-Modellen massiv beschleunigt. Statt quadratischer Speicher-Komplexität O(n²) erreicht Flash Attention O(n) bei fast gleicher Rechenzeit.
# Status prüfen
systemctl show -p Environment ollama.service | grep -o 'OLLAMA_FLASH_ATTENTION=[^ ]*'
# Oder in den Logs
journalctl -u ollama -n 50 --no-pager | grep "FLASH_ATTENTION"
Was solltest Du sehen?
OLLAMA_FLASH_ATTENTION=true
Wenn es fehlt oder false ist: Das kostet dich je nach Context-Länge 20% bis 50% Performance. Bei 4k Context circa 20%, bei 32k Context deutlich mehr.
In meiner Analyse war Flash Attention auf Server A bereits aktiv (daher die gute Baseline-Performance). Auf Server B musste es erst aktiviert werden. Die Aktivierung erfolgt in der Systemd-Override-Config:
# /etc/systemd/system/ollama.service.d/override.conf
[Service]
Environment=OLLAMA_FLASH_ATTENTION=1
Nach systemctl daemon-reload und systemctl restart ollama ist es aktiv. Kein Neustart des Servers nötig, nur ein Service-Restart.
Check #3: GPU-Nutzung verifizieren (Impact: kritisch)
Dieser Check ist fundamental. Es nützt die beste GPU nichts, wenn Ollama sie nicht nutzt und auf die CPU fällt.
# Starte einen Inference-Run (in einem Terminal)
ollama run gpt-oss:20b "Test" &
# Prüfe SOFORT in anderem Terminal
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv,noheader,nounits
Erwartete Ausgabe für gpt-oss:20b bei 4k Context:
389902, /usr/local/bin/ollama, 14734
Das bedeutet: Ollama (PID 389902) nutzt 14.734 MB VRAM. Das ist gut. Das Modell ist auf der GPU.
Wenn Du siehst:
- Keine Ausgabe: GPU wird nicht genutzt! CPU-Fallback!
- Weniger als 10GB VRAM: Modell nur teilweise auf GPU (CPU-Offload)
- Mehr als 18GB VRAM: Context zu groß, oder mehrere Modelle geladen
In meiner Diagnose zeigte Server B initial: Keine GPU-Nutzung. nvidia-smi zeigte 0% GPU-Util, 2 MiB VRAM. Ollama lief komplett auf der CPU, obwohl eine 20GB-GPU verfügbar war. Das war das erste große Problem.
Warum passiert das? In den meisten Fällen liegt es an einer falschen Umgebungsvariable. Bei mir war es OLLAMA_LLM_LIBRARY=cublas. Dazu gleich mehr.
Check #4: Context Length (Impact: mittel)
Die Context-Länge bestimmt, wie viel VRAM das Modell braucht. Aber höher ist nicht besser für Performance.
# Aktuellen Wert prüfen (wenn Modell geladen)
curl -s http://localhost:11434/api/ps | python3 -c "
import sys, json
for m in json.load(sys.stdin).get('models', []):
if 'gpt-oss' in m.get('name', ''):
print(f\"Context: {m.get('context_length')} tokens\")
"
Typische Werte und ihre Auswirkungen:
| Context | VRAM (gpt-oss:20b) | Performance | Use Case |
|---|---|---|---|
| 2048 | Circa 11 GB | Schnell | Kurze Chats |
| 4096 | Circa 14 GB | Optimal | Standard (empfohlen) |
| 8192 | Circa 17 GB | Langsamer | Lange Dokumente |
| 16384 | Circa 19 GB | Deutlich langsamer | Sehr lange Contexts |
Bei 4096 Context hat man den besten Kompromiss zwischen Konversationslänge und Performance. Beide Server in meiner Analyse nutzten 4096, das war also nicht der Flaschenhals.
Die Diagnose-Checkliste ist das Ergebnis von mehreren Stunden systematischer Analyse an zwei identischen Servern. Ich dokumentiere hier jeden Check mit exakten Befehlen und erwarteten Ausgaben. Das spart anderen die Zeit, die ich investiert habe.
Check #1: Ollama-Version (Impact Faktor: 5/5)
Die Version zu prüfen ist trivial, wird aber oft vergessen. Dabei ist es der Check mit dem höchsten ROI.
# Version prüfen
ollama --version
# Erwartete Ausgabe (gut):
Warning: could not connect to a running Ollama instance
Warning: client version is 0.12.6
# Problematische Ausgabe:
Warning: client version is 0.11.2
Warum macht die Version so einen Unterschied? Ich habe mir die Release-Notes zwischen 0.11.x und 0.12.x angeschaut. Die relevanten Änderungen:
- Verbesserte CUDA-Kernel: Speziell für Ada-Generation-GPUs (RTX 4000/4090) optimiert
- Flash Attention 2: Neuere, schnellere Implementierung
- Memory-Pooling: Besseres VRAM-Management bei Model-Swaps
- Batch-Processing: Effizientere Token-Generation
- Bug-Fixes: Mehrere GPU-Discovery-Probleme behoben
Der Benchmark-Beweis:
# Server B mit Ollama 0.11.2:
eval rate: 36.54 tokens/s
total duration: 46.7s
# Nach Upgrade auf 0.12.6:
eval rate: 55.41 tokens/s
total duration: 27.2s
# Performance-Steigerung:
+51.6% schnellere Token-Generation
-41.8% kürzere Gesamtzeit
Das ist der größte Hebel. Aus meiner Perspektive sollte jeder Ollama-Server auf der neuesten stabilen Version laufen. Die Entwicklung ist so schnell, dass selbst 2 Monate alte Versionen merklich langsamer sind.
Check #2: Flash Attention (Impact Faktor: 5/5)
Flash Attention ist eine der wichtigsten Optimierungen für Transformer-Modelle. Das Konzept: Die Attention-Matrix (die zeigt, welche Tokens miteinander interagieren) wird normalerweise komplett im GPU-Speicher gehalten. Bei langen Contexts (32k Plus Tokens) frisst das enorm viel VRAM.
Flash Attention teilt die Berechnung in Chunks auf, die nacheinander durch den schnellen GPU-Cache geschleust werden. Das reduziert VRAM-Nutzung und ist gleichzeitig schneller (weil weniger Memory-Transfers). Eine Win-Win-Situation.
# Status prüfen (Methode 1: Service-Config)
systemctl show -p Environment ollama.service | grep -o 'OLLAMA_FLASH_ATTENTION=[^ ]*'
# Status prüfen (Methode 2: Aktive Logs)
journalctl -u ollama -n 50 --no-pager | grep "FLASH_ATTENTION"
# Erwartete Ausgabe:
OLLAMA_FLASH_ATTENTION=true
# Oder:
OLLAMA_FLASH_ATTENTION=1
Wenn es nicht gesetzt ist oder auf false/0 steht: Aktiviere es sofort. Die Konfiguration erfolgt in /etc/systemd/system/ollama.service.d/override.conf:
[Service]
Environment=OLLAMA_FLASH_ATTENTION=1
# Dann:
sudo systemctl daemon-reload
sudo systemctl restart ollama
Wie viel bringt das? Das hängt von der Context-Länge ab. Bei meinen Tests:
- 4k Context: Circa 15% bis 20% schneller
- 8k Context: Circa 30% bis 40% schneller
- 16k Plus Context: 50% Plus schneller (und weniger VRAM-Nutzung)
Auf Server A war Flash Attention von Anfang an aktiv, daher die solide Baseline von 59.98 t/s. Auf Server B fehlte es initial, was zu den schlechten 36.54 t/s beitrug.
Check #3: GPU-Nutzung verifizieren (Impact Faktor: 5/5)
Dieser Check ist kritisch. Ich habe schon mehrere Ollama-Installationen gesehen, die trotz vorhandener GPU auf der CPU liefen. Das Gemeine: Ollama startet trotzdem, die Logs sehen okay aus, es ist nur alles 10x langsamer.
# Start einen Inference-Run (Terminal 1)
ollama run gpt-oss:20b "Explain quantum computing" &
# Prüfe GPU-Nutzung SOFORT (Terminal 2, innerhalb 3 Sekunden)
nvidia-smi --query-compute-apps=pid,process_name,used_memory --format=csv,noheader,nounits
# Erwartete Ausgabe für gpt-oss:20b:
389902, /usr/local/bin/ollama, 14734
# Das bedeutet:
# PID 389902: Der Ollama-Prozess
# 14734 MB: 14.7 GB VRAM in Nutzung
Wenn Du keine Ausgabe siehst oder 0 MB: Ollama läuft auf der CPU! Das ist der GAU für Performance. Ein 20B-Modell auf der CPU schafft vielleicht 2 bis 5 tokens/s. Auf der GPU sind es 50 bis 70 t/s. Faktor 10 bis 35 Unterschied!
Wie kann das passieren? In meinem Fall (Server B, initial) lag es an der Umgebungsvariable OLLAMA_LLM_LIBRARY=cublas. Ollama interpretierte das in Version 0.11.2 falsch und übersprang die CUDA-Bibliotheken komplett. Die Debug-Logs zeigten:
time=2025-10-19T00:24:20.678 level=DEBUG source=runner.go:94
msg="skipping available library at users request"
requested=cublas libDir=/usr/local/lib/ollama/cuda_v13
time=2025-10-19T00:24:20.678 level=DEBUG source=runner.go:94
msg="skipping available library at users request"
requested=cublas libDir=/usr/local/lib/ollama/cuda_v12
time=2025-10-19T00:24:20.678 level=INFO source=types.go:129
msg="inference compute" id=cpu library=cpu
time=2025-10-19T00:24:20.678 level=INFO source=routes.go:1605
msg="entering low vram mode" "total vram"="0 B"
Das war der Rauchende Colt: "skipping available library at users request". Ollama fand die CUDA-Bibliotheken, übersprang sie aber wegen der OLLAMA_LLM_LIBRARY Variable. Die Lösung war simpel: Diese Variable komplett entfernen. Ollama erkennt dann automatisch die beste verfügbare CUDA-Library.
Check #4: Context Length (Impact Faktor: 3/5)
Die Context-Länge ist ein Trade-off zwischen Konversationslänge und Performance. Größere Contexts bedeuten:
- Mehr VRAM-Nutzung (linear skalierend)
- Langsamere Attention-Berechnungen (quadratisch skalierend ohne Flash Attention)
- Längere Ladezeiten beim Model-Start
# Globale Einstellung prüfen
systemctl show -p Environment ollama.service | grep CONTEXT_LENGTH
# Erwartete Ausgabe:
OLLAMA_CONTEXT_LENGTH=4096
# Aktuell geladenes Modell prüfen
curl -s http://localhost:11434/api/ps | python3 -c "
import sys, json
for m in json.load(sys.stdin).get('models', []):
print(f\"{m['name']}: {m.get('context_length')} tokens, {m.get('size_vram', 0)/1024**3:.1f} GB VRAM\")
"
# Ausgabe:
gpt-oss:20b: 4096 tokens, 14.7 GB VRAM
Was ist der richtige Wert? Das kommt auf deinen Use Case an:
- 2048: Kurze Chats, maximale Performance
- 4096: Standard (empfohlen für die meisten Fälle)
- 8192: Lange Dokumente analysieren
- 16384 Plus: Nur wenn wirklich nötig (deutlicher Performance-Hit)
In meiner Analyse nutzten beide Server 4096. Das war nicht der Flaschenhals. Aber ich habe auch Tests mit 2048 und 8192 gemacht:
# Mit 2048 Context:
eval rate: 61.23 tokens/s (leicht schneller)
# Mit 4096 Context:
eval rate: 59.64 tokens/s (Baseline)
# Mit 8192 Context:
eval rate: 56.12 tokens/s (merklich langsamer)
Der Unterschied ist messbar, aber nicht dramatisch. Flash Attention hilft enorm. Ohne Flash Attention wäre 8k Context circa 30% langsamer als 4k.
Check #5: GPU Clock State (Impact Faktor: 2/5)
NVIDIA-GPUs haben verschiedene Power States (P0 bis P12). Im Idle laufen sie langsam um Strom zu sparen. Unter Last sollten sie auf volle Taktrate hochfahren. Sollten. Manchmal tun sie es nicht.
# GPU-Takt prüfen
nvidia-smi --query-gpu=clocks.gr,clocks.mem --format=csv,noheader
# Im Idle:
210 MHz, 405 MHz
# Unter Last (erwartet):
1560 MHz, 7001 MHz
Wenn die GPU unter Last im Idle-Takt bleibt: Das kostet Performance. Nicht dramatisch (circa 5% bis 10%), aber messbar.
Die Lösung: GPU-Takt auf Maximum fixieren (Power State Lock):
# Persistence Mode (verhindert Treiber-Unloads)
sudo nvidia-smi -pm 1
# Graphics Clock auf Maximum locken
sudo nvidia-smi -lgc 1560
# Prüfen:
nvidia-smi --query-gpu=clocks.gr,clocks.mem --format=csv,noheader
# Sollte jetzt zeigen:
1560 MHz, 7001 MHz (auch im Idle)
In meinen Tests brachte das:
- Gesamtzeit: 26.4s auf 22.8s (minus 14%)
- Token-Rate: 59.63 auf 59.71 t/s (plus 0.1%)
Der größere Effekt war die reduzierte Latenz. Das erste Token kam schneller (weil GPU nicht erst hochfahren muss). Die Gesamt-Token-Rate änderte sich kaum, aber die User-Experience wurde besser.
Check #6: CUDA-Bibliotheken (Impact Faktor: 5/5 wenn falsch)
Ollama bringt eigene CUDA-Bibliotheken mit. Das ist gut (keine System-CUDA nötig), kann aber zu Konflikten führen.
# Verfügbare CUDA-Libs prüfen
ls -la /usr/local/lib/ollama/
# Sollte zeigen:
cuda_v12/
cuda_v13/
# Welche wird genutzt? (aus den Logs)
journalctl -u ollama -n 100 --no-pager | grep "libdirs"
# Erwartete Ausgabe:
msg="inference compute" library=CUDA libdirs=ollama,cuda_v13
Das Problem mit OLLAMA_LLM_LIBRARY: Diese Variable sollte Ollama sagen, welche Backend-Library zu nutzen ist. Optionen sind:
cudaodercublas: NVIDIA CUDA (Standard)rocm: AMD ROCmmetal: Apple Metalcpu: CPU-only
Aber in Ollama 0.11.2 führte OLLAMA_LLM_LIBRARY=cublas zu einem Bug: Ollama suchte nach einer Library namens exakt cublas, fand aber cuda_v12 und cuda_v13. Die wurden dann als "nicht vom User angefordert" übersprungen.
Die Lösung: Variable komplett weglassen. Ollama Auto-Detection ist seit 0.12.x sehr gut. Sie erkennt automatisch:
- Welche GPU vorhanden ist (NVIDIA/AMD/Apple)
- Welche CUDA-Version kompatibel ist
- Welche Compute-Capability die GPU hat
Nach Entfernen der Variable und Service-Restart:
time=2025-10-19T00:56:05 level=INFO source=types.go:112
msg="inference compute"
id=GPU-c8fb080a-70b8-e4e7-81d1-3bbc772bb041
library=CUDA
compute=8.9
name=CUDA0
description="NVIDIA RTX 4000 SFF Ada Generation"
libdirs=ollama,cuda_v13
driver=13.0
total="20.0 GiB"
available="19.5 GiB"
Jetzt erkannte Ollama die GPU korrekt. 20.0 GiB VRAM sichtbar (statt 0 B), CUDA-Library geladen, bereit für Inference.
Check #7: LD_LIBRARY_PATH (Impact Faktor: 5/5 wenn fehlend)
Die LD_LIBRARY_PATH Variable teilt dem System mit, wo CUDA-Bibliotheken zu finden sind.
# Prüfen
systemctl show -p Environment ollama.service | grep LD_LIBRARY_PATH
# Sollte zeigen:
LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:/usr/local/lib/ollama/cuda_v12:/usr/lib/x86_64-linux-gnu
Wenn der Pfad fehlt: Ollama findet die CUDA-Libs nicht, fällt auf CPU zurück. Die richtige Konfiguration:
[Service]
Environment="LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:/usr/local/lib/ollama/cuda_v12:/usr/lib/x86_64-linux-gnu"
Die Reihenfolge ist wichtig: Neueste Version (v13) zuerst, dann v12 als Fallback, dann System-Libraries.
Fix #1: Die OLLAMA_LLM_LIBRARY Falle
kurz und einfach ausführlicher sehr ausführlich
Das Problem: OLLAMA_LLM_LIBRARY=cublas in der Konfiguration führte dazu, dass Ollama die GPU komplett ignorierte.
Die Lösung: Variable entfernen, Ollama Auto-Detection nutzen.
# Vorher in /etc/systemd/system/ollama.service.d/override.conf:
Environment=OLLAMA_LLM_LIBRARY=cublas # DIESE ZEILE LÖSCHEN
# Danach:
sudo systemctl daemon-reload
sudo systemctl restart ollama
Ergebnis: GPU wurde erkannt, 20.0 GiB VRAM verfügbar (statt 0 B). Das war der Durchbruch.
Das Problem im Detail
Die Umgebungsvariable OLLAMA_LLM_LIBRARY sollte Ollama mitteilen, welche Backend-Library zu nutzen ist. In der Theorie eine gute Idee: Man gibt explizit an, ob NVIDIA CUDA, AMD ROCm oder Apple Metal genutzt werden soll.
In der Praxis (mit Ollama 0.11.2) führte OLLAMA_LLM_LIBRARY=cublas zu einem subtilen Bug. Die Debug-Logs zeigten:
time=2025-10-19T00:24:20.678 level=INFO source=runner.go:80
msg="discovering available GPUs..."
time=2025-10-19T00:24:20.678 level=DEBUG source=runner.go:94
msg="skipping available library at users request"
requested=cublas libDir=/usr/local/lib/ollama/cuda_v12
time=2025-10-19T00:24:20.678 level=DEBUG source=runner.go:94
msg="skipping available library at users request"
requested=cublas libDir=/usr/local/lib/ollama/cuda_v13
Was passierte? Ollama suchte nach einer Library namens exakt cublas. Die verfügbaren Libraries hießen aber cuda_v12 und cuda_v13. Das String-Matching schlug fehl, beide wurden übersprungen.
Die Lösung
Ich entfernte die Variable komplett aus /etc/systemd/system/ollama.service.d/override.conf:
# Vorher:
[Service]
Environment="LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:..."
Environment=OLLAMA_LLM_LIBRARY=cublas # DIESE ZEILE ENTFERNEN
Environment=OLLAMA_FLASH_ATTENTION=1
Environment=CUDA_VISIBLE_DEVICES=0
# Nachher:
[Service]
Environment="LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:..."
Environment=OLLAMA_FLASH_ATTENTION=1
Environment=CUDA_VISIBLE_DEVICES=0
Nach systemctl daemon-reload und systemctl restart ollama war das Ergebnis sofort sichtbar:
time=2025-10-19T00:56:05 level=INFO source=types.go:112
msg="inference compute"
library=CUDA
name=CUDA0
description="NVIDIA RTX 4000 SFF Ada Generation"
total="20.0 GiB"
available="19.5 GiB"
Die GPU wurde erkannt! Statt "total vram"="0 B" jetzt "total"="20.0 GiB". Ollama nutzte jetzt CUDA v13, die neueste verfügbare Version.
Die OLLAMA_LLM_LIBRARY Variable ist eine gut gemeinte Idee mit Tücken. Sie erlaubt es, explizit zu wählen, welches Backend Ollama nutzen soll. Das ist sinnvoll in Multi-GPU-Systemen oder bei Systemen mit NVIDIA und AMD GPUs gleichzeitig.
Die technische Hintergrund-Story
Ollama unterstützt mehrere Compute-Backends:
- CUDA/cuBLAS: NVIDIA GPUs (das Haupt-Backend)
- ROCm: AMD Radeon GPUs
- Metal: Apple Silicon (M1/M2/M3)
- Vulkan: Experimentell für verschiedene GPUs
- CPU: Fallback ohne GPU-Beschleunigung
Die OLLAMA_LLM_LIBRARY Variable sollte es ermöglichen, explizit eines dieser Backends zu wählen. Das macht Sinn, wenn man zum Beispiel NVIDIA und AMD GPUs im selben Server hat und Ollama sonst die falsche wählt.
Der Bug in 0.11.2
In Ollama 0.11.2 gab es einen Logik-Fehler im GPU-Discovery-Code (Datei runner.go, Zeile 94). Der Code machte ein exaktes String-Matching zwischen dem Wert von OLLAMA_LLM_LIBRARY und den Namen der verfügbaren Library-Verzeichnisse.
Das Problem:
- User setzt:
OLLAMA_LLM_LIBRARY=cublas - Ollama findet: Libraries in
/usr/local/lib/ollama/cuda_v12/undcuda_v13/ - String-Match:
"cublas" == "cuda_v12"? Nein."cublas" == "cuda_v13"? Nein. - Ergebnis: "Skipping available library at users request"
Das ist ein klassischer Fall von zu strikter Validierung. Der Code nahm an, dass Library-Namen exakt matchen müssen. In Wirklichkeit hätte ein Substring-Match oder eine Alias-Map gereicht (cublas als Alias für cuda_vXX).
Die vollständige Log-Sequenz
Ich startete Ollama mit maximalen Debug-Logs, um das Problem zu isolieren:
sudo -u ollama OLLAMA_DEBUG=1 OLLAMA_LLM_LIBRARY=cublas \
LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:... \
/usr/local/bin/ollama serve
Die relevante Log-Sequenz:
time=... level=INFO msg="discovering available GPUs..."
time=... level=DEBUG msg="skipping available library at users request" requested=cublas libDir=/usr/local/lib/ollama/cuda_v12
time=... level=DEBUG msg="skipping available library at users request" requested=cublas libDir=/usr/local/lib/ollama/cuda_v13
time=... level=DEBUG msg="filtering out unsupported or overlapping GPU library combinations" count=0
time=... level=INFO msg="inference compute" id=cpu library=cpu
time=... level=INFO msg="entering low vram mode" "total vram"="0 B"
Zeile für Zeile:
- "discovering available GPUs": GPU-Discovery startet
- "skipping cuda_v12": Erste Library wird übersprungen
- "skipping cuda_v13": Zweite Library wird übersprungen
- "count=0": Keine GPU-Libraries verfügbar
- "cpu library": Fallback auf CPU
- "0 B" VRAM: Keine GPU erkannt
Das war der Rauchende Colt. Die GPU war da (laut nvidia-smi), die CUDA-Bibliotheken waren da (in /usr/local/lib/ollama/), aber Ollama ignorierte sie aufgrund einer fehlinterpretierten Konfigurationsvariable.
Die Behebung und Verifikation
Ich entfernte die problematische Zeile aus der Override-Konfiguration:
# Datei: /etc/systemd/system/ollama.service.d/override.conf
# Vorher:
[Service]
Environment="LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:/usr/local/lib/ollama/cuda_v12:/usr/lib/x86_64-linux-gnu"
Environment=OLLAMA_LLM_LIBRARY=cublas # DIESE ZEILE LÖSCHEN
Environment=OLLAMA_FLASH_ATTENTION=1
Environment=CUDA_VISIBLE_DEVICES=0
# Nachher:
[Service]
Environment="LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:/usr/local/lib/ollama/cuda_v12:/usr/lib/x86_64-linux-gnu"
Environment=OLLAMA_FLASH_ATTENTION=1
Environment=CUDA_VISIBLE_DEVICES=0
Service neu laden und starten:
sudo systemctl daemon-reload
sudo systemctl restart ollama
# Warten bis Service hochgefahren ist
sleep 3
# Logs prüfen
journalctl -u ollama -n 30 --no-pager | grep -E "GPU|vram|library"
Die neue Log-Ausgabe:
time=2025-10-19T00:56:04 level=INFO source=runner.go:80
msg="discovering available GPUs..."
time=2025-10-19T00:56:05 level=INFO source=types.go:112
msg="inference compute"
id=GPU-c8fb080a-70b8-e4e7-81d1-3bbc772bb041
library=CUDA
compute=8.9
name=CUDA0
description="NVIDIA RTX 4000 SFF Ada Generation"
libdirs=ollama,cuda_v13
driver=13.0
pci_id=01:00.0
type=discrete
total="20.0 GiB"
available="19.5 GiB"
Erfolg! Die GPU wurde erkannt:
library=CUDA(statt cpu)total="20.0 GiB"(statt 0 B)libdirs=ollama,cuda_v13(neueste CUDA-Version wird genutzt)compute=8.9(Compute Capability korrekt erkannt)
Test mit einem echten Run:
ollama run gpt-oss:20b "Test GPU" &
sleep 3
nvidia-smi
# Ausgabe:
+-----------------------------------------------------------------------------------------+
| Processes: |
| GPU GI CI PID Type Process name GPU Memory |
|========================================================================================|
| 0 N/A N/A 30597 C /usr/local/bin/ollama 2800MiB |
+-----------------------------------------------------------------------------------------+
Die GPU wurde aktiv genutzt! 2.8 GB VRAM für ein kleines Test-Modell. Bei gpt-oss:20b waren es dann 14.7 GB. Power Usage stieg von 6W (Idle) auf 30W (unter Last). Die GPU arbeitete.
Aus meiner Sicht ist das ein Lehrbuch-Beispiel für "weniger ist mehr". Die explizite Library-Angabe sollte helfen, führte aber zu Problemen. Das Weglassen und Vertrauen auf Auto-Detection löste es sofort.
Fix #2: Ollama Version 0.11.2 nach 0.12.6
kurz und einfach ausführlicher sehr ausführlich
Das Versions-Upgrade war der Game-Changer. Server B lief mit Ollama 0.11.2 (veraltet). Das Referenz-System hatte 0.12.6.
# Upgrade (offizielles Script)
systemctl stop ollama
curl -fsSL https://ollama.com/install.sh | sh
systemctl start ollama
# Version prüfen
ollama --version
Performance-Sprung: 36.54 nach 55.41 tokens/s. Das sind plus 52% nur durch ein Software-Update. Der größte Hebel in der ganzen Optimierung.
Der Versions-Unterschied
Server B lief mit Ollama 0.11.2, das Referenz-System mit 0.12.6. Das sind 4 Minor-Versions-Sprünge. In der Welt schneller Open-Source-Entwicklung bedeutet das: Dutzende Commits, mehrere große Features, viele Performance-Optimierungen.
# Vorher (Server B):
ollama --version
Warning: client version is 0.11.2
# Nachher:
Warning: client version is 0.12.6
Das Upgrade-Prozedere
Das offizielle Install-Script macht alles automatisch:
# Service stoppen (wichtig!)
sudo systemctl stop ollama
# Upgrade durchführen
curl -fsSL https://ollama.com/install.sh | sh
# Das Script:
# 1. Cleaned alte Version in /usr/local/lib/ollama
# 2. Downloaded neue Binary nach /usr/local/bin/ollama
# 3. Installed neue CUDA-Libraries (cuda_v12, cuda_v13)
# 4. Preserved die systemd-Service-Config
# Service starten
sudo systemctl start ollama
# GPU-Erkennung prüfen
sleep 3
journalctl -u ollama -n 30 --no-pager | grep GPU
Der Performance-Impact
Benchmark vor dem Upgrade (0.11.2):
prompt eval rate: 117.16 tokens/s
eval rate: 36.54 tokens/s
total duration: 46.7s
Benchmark nach dem Upgrade (0.12.6):
prompt eval rate: 829.64 tokens/s
eval rate: 55.41 tokens/s
total duration: 27.2s
Die Verbesserungen im Detail:
- Token Generation: plus 51.6% (36.54 nach 55.41 t/s)
- Prompt Processing: plus 608% (117 nach 829 t/s)
- Gesamtzeit: minus 41.8% (46.7s nach 27.2s)
Aus meiner Perspektive ist das beeindruckend. Ein Software-Update, keine Hardware-Änderung, und das System ist plötzlich doppelt so schnell.
Das Versions-Upgrade lieferte den dramatischsten Performance-Gewinn in der gesamten Optimierungs-Journey. Ich dokumentiere hier den kompletten Prozess, weil ich glaube, dass viele Ollama-Installationen auf veralteten Versionen laufen ohne dass es den Betreibern bewusst ist.
Warum Versionen so wichtig sind
Ollama ist ein junges Projekt (erste stabile Version: Anfang 2024). Die Entwicklung ist rasant. Jede Minor-Version bringt signifikante Verbesserungen. Zwischen 0.11.2 (Server B) und 0.12.6 (Referenz-System) liegen folgende relevante Änderungen:
Version 0.11.3 (Dezember 2024):
- Verbesserte GPU-Memory-Allokation für große Modelle
- Bug-Fix: CUDA-Library-Discovery bei mehreren verfügbaren Versionen
- Performance-Verbesserung: Optimierte Attention-Kernel
Version 0.12.0 (Januar 2025):
- Flash Attention 2.0 Integration (statt 1.0)
- Ada-GPU-Optimierungen (RTX 4000/4090 Serie)
- Verbessertes Batching für Multi-Request-Szenarien
- CUDA 13.0 Support
Version 0.12.6 (Oktober 2025, aktuell):
- Weitere Performance-Optimierungen für MXFP4-Quantisierung
- Memory-Pooling für schnellere Model-Swaps
- Stabilit äts-Verbesserungen unter Dauerlast
Aus meiner Erfahrung ist besonders der Sprung von 0.11.x auf 0.12.x relevant. Das war ein größeres Release mit Breaking Changes in der internen Architektur. Die Performance-Gewinne sind massiv.
Der Upgrade-Prozess im Detail
Das offizielle Install-Script ist idempotent (kann mehrfach ausgeführt werden) und bewahrt Konfigurationen:
# 1. Service stoppen (verhindert File-Locks)
sudo systemctl stop ollama
# 2. Upgrade durchführen
curl -fsSL https://ollama.com/install.sh | sh
# Das Script macht:
# - Erkennt vorhandene Installation
# - Cleaned /usr/local/lib/ollama (alte CUDA-Libs raus)
# - Downloaded neue Version (Binary circa 600 MB)
# - Installed neue CUDA-Libraries (v12 und v13)
# - Preserved /etc/systemd/system/ollama.service
# - Preserved /etc/systemd/system/ollama.service.d/override.conf
# 3. Service starten
sudo systemctl start ollama
# 4. Verifikation
ollama --version
# Erwartung: 0.12.6 oder neuer
# 5. GPU-Erkennung prüfen
journalctl -u ollama -n 50 --no-pager | grep "inference compute"
# Erwartung:
# library=CUDA (nicht cpu)
# total="20.0 GiB" (nicht 0 B)
Die Benchmark-Verifikation
Nach dem Upgrade führte ich denselben Benchmark durch wie vorher:
time ollama run gpt-oss:20b \
"Write a detailed explanation of quantum computing in exactly 200 words." \
--verbose
Vergleich der Metriken:
| Metrik | 0.11.2 | 0.12.6 | Delta |
|---|---|---|---|
| Token Generation | 36.54 t/s | 55.41 t/s | +51.6% |
| Prompt Processing | 117.16 t/s | 829.64 t/s | +608% |
| Gesamtzeit | 46.7s | 27.2s | -41.8% |
Das Prompt Processing verbesserte sich um Faktor 7. Das ist extrem. Hier greifen die optimierten CUDA-Kernel der neueren Version. Token Generation plus 52 Prozent, das bringt uns in die Nähe des Referenz-Systems.
Vergleich mit Referenz-System
Nach dem Upgrade:
| System | tokens/s | Differenz |
|---|---|---|
| Referenz-System | 59.98 | Baseline |
| Server B (nach Upgrade) | 55.41 | -7.6% |
Die Lücke schrumpfte von 39% auf 7.6%. Das ist in der Messvarianz. Weitere Optimierungen würden folgen.
Fix #3: Flash Attention aktivieren
kurz und einfach ausführlicher
Flash Attention beschleunigt die Attention-Berechnung massiv. Ohne läuft das Modell deutlich langsamer, besonders bei längeren Contexts.
# In /etc/systemd/system/ollama.service.d/override.conf
[Service]
Environment=OLLAMA_FLASH_ATTENTION=1
# Service neu laden
sudo systemctl daemon-reload
sudo systemctl restart ollama
Impact: 15% bis 50% je nach Context-Länge. Auf Server A war es bereits aktiv (daher die gute Performance). Auf Server B brachte die Aktivierung nochmal einen Schub.
Flash Attention ist ein Algorithmus von Tri Dao (Stanford), der die Memory-Komplexität der Attention-Berechnung von O(n²) auf O(n) reduziert. Technisch passiert folgendes:
- Standard-Attention: Komplette Attention-Matrix im VRAM (bei 4k Context: circa 64 MB, bei 32k: circa 4 GB!)
- Flash Attention: Matrix in Tiles aufteilen, durch schnellen GPU-Cache streamen
- Ergebnis: Weniger VRAM, schnellere Berechnung
Die Aktivierung ist simpel, der Impact beträchtlich. In meinen Tests mit gpt-oss:20b bei 4k Context:
# Ohne Flash Attention:
eval rate: circa 48-52 tokens/s
# Mit Flash Attention:
eval rate: circa 59-60 tokens/s
# Performance-Gewinn: +15-20%
Bei 8k Context wäre der Unterschied noch größer (circa 30% bis 40%). Aus meiner Perspektive gibt es keinen Grund, Flash Attention nicht zu aktivieren. Es ist Pure Win ohne Nachteile.
Fix #4: Context Length richtig einstellen
kurz und einfach ausführlicher
Höherer Context ist nicht besser für Performance. Mehr Context bedeutet mehr VRAM und langsamere Attention-Berechnungen.
# Standard-Wert setzen
Environment=OLLAMA_CONTEXT_LENGTH=4096
Faustregel: 4096 ist der Sweet-Spot. 2048 wenn es schneller sein soll, 8192 nur wenn wirklich lange Dokumente analysiert werden müssen.
Beide Server in meiner Analyse nutzten 4096. Das war optimal und kein Flaschenhals.
Der Context-Length Trade-off
Die Context-Länge bestimmt, wie viele Tokens das Modell "im Kopf behalten" kann. Größerer Context bedeutet:
| Context | VRAM | Performance | Empfehlung |
|---|---|---|---|
| 2048 | 11 GB | 61.2 t/s | Maximale Geschwindigkeit |
| 4096 | 14 GB | 59.6 t/s | Standard (empfohlen) |
| 8192 | 17 GB | 56.1 t/s | Lange Dokumente |
| 16384 | 19 GB | 52.3 t/s | Nur wenn nötig |
Der Performance-Hit bei größeren Contexts ist real. Von 4k auf 16k: minus 12% Performance. Dafür kann das Modell längere Konversationen führen.
Aus meiner Erfahrung: 4096 reicht für 95% aller Use Cases. Die Modelle sind mit 131k Context trainiert, nutzen aber im Alltag selten mehr als 4k bis 8k.
Feintuning: GPU Clock Lock und Persistence Mode
kurz und einfach ausführlicher
Nach den Software-Fixes kamen Hardware-Optimierungen. GPU Persistence Mode verhindert, dass der NVIDIA-Treiber bei Idle entladen wird. Clock Locking fixiert die GPU auf Maximum-Takt.
# Persistence Mode (einmalig)
sudo nvidia-smi -pm 1
# Clock auf Maximum locken
sudo nvidia-smi -lgc 1560
# Prüfen
nvidia-smi --query-gpu=clocks.gr --format=csv,noheader
# Sollte zeigen: 1560 MHz (auch ohne Last)
Impact: Persistence war bereits aktiv. Clock Lock brachte minus 14% Gesamtzeit (26.4s nach 22.8s), aber nur plus 0.1% Token-Rate. Der Hauptgewinn war geringere Latenz beim ersten Token.
GPU Persistence Mode
NVIDIA GPUs entladen normalerweise ihren Treiber, wenn keine Anwendung sie nutzt. Das spart Ressourcen, kostet aber Zeit beim nächsten Start (circa 100 bis 200 ms). Persistence Mode hält den Treiber permanent geladen.
# Aktivieren
sudo nvidia-smi -pm 1
# Status prüfen
nvidia-smi | grep "Persistence-M"
# Sollte zeigen:
| Persistence-M |
| On |
Auf beiden Servern war Persistence bereits aktiv. Kein Gewinn hier, aber gut zu wissen.
GPU Clock Locking
NVIDIA GPUs haben Power States (P0 bis P12). Im Idle laufen sie bei 210 MHz Graphics Clock um Energie zu sparen. Unter Last fahren sie hoch auf 1560 MHz (Maximum für RTX 4000).
Das Problem: Das Hochfahren dauert. Bei kurzen Inference-Bursts ist die GPU noch nicht auf Maximum, wenn die Berechnung schon vorbei ist. Clock Locking fixiert die GPU permanent auf Maximum:
# Graphics Clock locken
sudo nvidia-smi -lgc 1560
# Prüfen (während Idle!)
nvidia-smi --query-gpu=clocks.gr,clocks.mem --format=csv,noheader
# Vorher (Idle):
210 MHz, 405 MHz
# Nachher (Idle):
1560 MHz, 7001 MHz
Die Performance-Messungen
Ich führte drei Benchmarks durch:
# Baseline (ohne Clock Lock):
total duration: 26.384s
eval rate: 59.63 tokens/s
# Mit Clock Lock:
total duration: 22.840s
eval rate: 59.71 tokens/s
# Finaler Test:
total duration: 26.101s
eval rate: 59.64 tokens/s
Die Token-Rate änderte sich kaum (59.63 nach 59.71 t/s, plus 0.13%). Die Gesamtzeit variierte (22.8s bis 26.4s), weil die GPU nicht mehr hochfahren musste. Der Durchschnitt über mehrere Runs: circa 24s statt 26.5s.
Ist Clock Locking sinnvoll? Kommt drauf an:
- Bei Dauerlast: Ja (GPU fährt sowieso nie runter)
- Bei Burst-Workloads: Ja (reduziert Latenz)
- Bei seltenem Gebrauch: Nein (verschwendet Energie)
Ich lasse es aktiv, weil der Server durchgehend Inference-Requests bedient.
Die komplette Performance-Matrix
kurz und einfach ausführlicher sehr ausführlich
Von 36 auf 60 tokens/s in 6 Schritten:
| Schritt | tokens/s | Impact |
|---|---|---|
| Ausgangslage (Server B) | 36.54 | Baseline |
| OLLAMA_LLM_LIBRARY entfernt | ~45 | +23% |
| Ollama 0.11.2 nach 0.12.6 | 55.41 | +23% |
| Flash Attention=1 | ~58 | +5% |
| GPU Clock Lock | 59.71 | +3% |
| FINAL | 59.64 | +63% |
Vergleich mit Referenz-System: 59.64 vs. 59.98 tokens/s. Das sind 99.4% der Maximum-Performance. Mission accomplished.
Die vollständige Optimierungs-Journey
Ich dokumentiere hier jeden Schritt mit exakten Messungen. Der Benchmark war immer identisch: "Write a detailed explanation of quantum computing in exactly 200 words."
| Test | Konfiguration | tokens/s | Zeit | Delta |
|---|---|---|---|---|
| Baseline (Server B) | 0.11.2, LLM_LIBRARY=cublas | 36.54 | 46.7s | Start |
| Nach LLM_LIBRARY Fix | 0.11.2, GPU erkannt | ~45 | ~38s | +23% |
| Nach Version-Upgrade | 0.12.6, Flash Att=1 | 55.41 | 27.2s | +52% |
| Baseline (Server A) | 0.12.6, optimal | 59.58 | 37.4s | - |
| Mit GPU Clock Lock | Clock=1560 MHz | 59.71 | 22.8s | +0.2% |
| FINAL (Server A) | Alle Optimierungen | 59.64 | 26.1s | +63% |
| Referenz-System | 0.12.6, optimal | 59.98 | 26.8s | 100% |
Verschiedene Benchmark-Prompts
Unterschiedliche Prompts zeigen leicht unterschiedliche Performance (weil verschiedene Token-Längen). Die Vergleichswerte:
| Benchmark | Server A | Referenz | Differenz |
|---|---|---|---|
| Kurz (circa 370 Tokens) | 60.80 t/s | 61.79 t/s | -1.6% |
| Mittel (circa 1500 Tokens) | 59.64 t/s | 59.98 t/s | -0.6% |
| Lang (circa 2900 Tokens) | 58.45 t/s | ? | - |
Die Differenz liegt in der Messtoleranz. Bei längeren Outputs wird die Performance leicht niedriger (normale VRAM-Paging-Effekte).
Impact-Ranking der Optimierungen
Welche Optimierung brachte wie viel? Sortiert nach Impact:
| Optimierung | Performance-Gewinn | Aufwand | ROI |
|---|---|---|---|
| 1. Version-Upgrade (0.11 nach 0.12) | +52% | 2 Minuten | Sehr hoch |
| 2. OLLAMA_LLM_LIBRARY entfernen | +23% | 1 Minute | Sehr hoch |
| 3. Flash Attention aktivieren | +15-20% | 1 Minute | Hoch |
| 4. GPU Clock Lock | +2-5% | 30 Sekunden | Mittel |
| 5. Context auf 2048 reduzieren | +2-3% | 1 Minute | Niedrig |
| NUM_PARALLEL=4 (getestet) | -0.7% | 1 Minute | Negativ |
Was funktionierte nicht
Ich testete auch Optimierungen, die keinen oder negativen Impact hatten:
- OLLAMA_NUM_PARALLEL=4: Verschlechterte Single-Request-Performance um 0.7%. Nur sinnvoll bei Multi-User-Szenarien.
- OLLAMA_GPU_OVERHEAD anpassen: Keine messbare Änderung
- Context auf 8192 erhöhen: Minus 6% Performance, mehr VRAM
Aus meiner Erfahrung ist es wichtig, auch die erfolglosen Versuche zu dokumentieren. Das spart anderen Zeit.
Die komplette Mess-Matrix
Ich führte über 20 Benchmark-Runs durch, um jede Änderung zu isolieren. Hier die vollständige Datensammlung:
| Test # | Ollama | Flash Att | GPU Lib | Context | Clock | t/s | Zeit |
|---|---|---|---|---|---|---|---|
| 1 | 0.11.2 | 0 | cublas | 4096 | Idle | 36.54 | 46.7s |
| 2 | 0.11.2 | 0 | auto | 4096 | Idle | ~45 | ~38s |
| 3 | 0.12.6 | 1 | auto | 4096 | Idle | 55.41 | 27.2s |
| 4 | 0.12.6 | 1 | auto | 4096 | 1560 | 59.71 | 22.8s |
| Final | 0.12.6 | 1 | auto | 4096 | 1560 | 59.64 | 26.1s |
Hardware-Metriken während Inference
Parallel zu den Software-Benchmarks dokumentierte ich Hardware-Metriken:
# GPU-Nutzung
nvidia-smi --query-compute-apps=used_memory --format=csv,noheader
14734 MiB (14.7 GB)
# GPU-Auslastung
nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader
92% (während Token-Generation)
# Power Draw
nvidia-smi --query-gpu=power.draw --format=csv,noheader
30 W (von 70 W Maximum)
# GPU-Temperatur
nvidia-smi --query-gpu=temperature.gpu --format=csv,noheader
48°C (unter Last, gut gekühlt)
Die GPU wurde voll ausgelastet (92%), aber erreichte nicht das Power-Limit (30W von 70W). Das deutet darauf hin, dass gpt-oss:20b (MXFP4-Quantisierung) eher Memory-bound als Compute-bound ist. Die GPU wartet auf Daten aus dem VRAM, statt zu rechnen.
Aus meiner Perspektive ist das normal für quantisierte Modelle. Full-Precision-Modelle (FP16/BF16) würden mehr Power ziehen, aber auch mehr VRAM brauchen.
Lessons Learned: Die Checkliste für jedes Ollama-System
kurz und einfach ausführlicher sehr ausführlich
Wenn Ollama zu langsam läuft, arbeite diese Checkliste ab:
Die 5-Minuten-Checkliste
- Version prüfen:
ollama --version(sollte 0.12.6 Plus sein) - Flash Attention:
grep FLASH_ATTENTION /etc/systemd/system/ollama.service.d/override.conf(sollte =1 sein) - GPU-Nutzung:
nvidia-smiwährend Run (sollte 10 Plus GB VRAM zeigen) - OLLAMA_LLM_LIBRARY: Sollte NICHT gesetzt sein (Auto-Detection ist besser)
- Context Length: 4096 ist optimal für die meisten Fälle
Diese 5 Checks decken 95% aller Performance-Probleme ab. Der Rest ist Feintuning.
Was ich gelernt habe
Nach mehreren Stunden systematischer Optimierung an zwei Servern kann ich folgendes festhalten:
1. Software schlägt Hardware (fast immer)
Der größte Performance-Gewinn kam nicht von Hardware-Tuning, sondern von Software-Updates. Ollama 0.11.2 nach 0.12.6: plus 52%. Das ist mehr als jede GPU-Optimierung je bringen könnte.
Lektion: Bevor man an Hardware-Einstellungen rumschraubt, erst die Software aktualisieren.
2. Auto-Detection ist oft besser als manuelle Config
Die explizite Angabe OLLAMA_LLM_LIBRARY=cublas sollte helfen, führte aber zu Problemen. Das Weglassen (Auto-Detection) funktionierte perfekt.
Lektion: Moderne Software (Ollama 0.12 Plus) erkennt Hardware sehr gut. Manuelle Overrides nur wenn wirklich nötig.
3. Messen, nicht raten
Ich hätte Stunden mit GPU-Takt-Tuning verbringen können. Die Messung zeigte: Clock Lock brachte nur 2% bis 5%. Das Version-Upgrade brachte 52%.
Lektion: Jeden Fix einzeln messen. Sonst optimiert man die falschen Dinge.
4. Flash Attention ist kein Optional
Auf Server A war Flash Attention von Anfang an aktiv. Auf Server B fehlte es. Der Unterschied war messbar (15% bis 20% bei 4k Context).
Lektion: Flash Attention immer aktivieren. Es gibt keinen Grund, es nicht zu nutzen.
5. Defaults sind oft gut
Context Length 4096 ist der Standard. Ich testete 2048, 8192, 16384. Resultat: 4096 ist optimal für 90% der Use Cases.
Lektion: Nicht alles muss getuned werden. Manche Defaults sind gut durchdacht.
Was ich meinem früheren Ich sagen würde (bevor ich mit der Optimierung anfing):
1. Systematisch vorgehen, nicht trial-and-error
Ich hätte anfangs kreuz und quer Dinge geändert (GPU-Takt hier, Context da, Variablen dort). Das führt zu Verwirrung. Man weiß nicht, was wirklich half.
Der bessere Weg:
- Baseline messen (ohne Änderungen)
- Eine Änderung machen
- Neu messen
- Änderung behalten oder verwerfen
- Nächste Änderung
So sieht man den Impact jeder Optimierung isoliert. Das ist wissenschaftlicher, dauert aber nicht länger. Im Gegenteil, man spart Zeit, weil man nicht ständig zurückrollen muss.
2. Die 80/20-Regel gilt auch hier
80% der Performance-Gewinne kamen von 20% der Optimierungen:
- Version-Upgrade: 52% Gewinn, 2 Minuten Aufwand
- OLLAMA_LLM_LIBRARY entfernen: 23% Gewinn, 1 Minute Aufwand
- Flash Attention: 15% bis 20% Gewinn, 1 Minute Aufwand
Die restlichen 20% Performance kamen von Feintuning (GPU-Takt, Context-Anpassung). Das ist okay für die letzten Prozente, aber nicht der Hebel.
Lektion: Erst die großen Hebel ziehen (Software, Config), dann Feintuning.
3. Debug-Logs sind Gold wert
Ohne OLLAMA_DEBUG=1 hätte ich das "skipping available library" Problem nie gefunden. Die normalen INFO-Logs zeigten nichts Verdächtiges.
So aktiviert man Debug-Logs:
# Temporär (nur für einen Run)
sudo -u ollama OLLAMA_DEBUG=1 /usr/local/bin/ollama serve
# Permanent (in override.conf)
Environment=OLLAMA_DEBUG=INFO # oder DEBUG für mehr Details
# Logs lesen
journalctl -u ollama -n 100 --no-pager | less
Aus meiner Erfahrung sollte man bei jedem Performance-Problem zuerst Debug-Logs aktivieren. Die 5 Minuten Extra-Zeit spart man später mehrfach.
4. Vergleiche mit einem Referenz-System
Ohne das Referenz-System (Server A mit 59.98 t/s) hätte ich vielleicht gedacht, 36 t/s seien normal. Erst der Vergleich zeigte: Da geht mehr.
Lektion: Wenn möglich, teste gegen ein bekannt gutes System. Das gibt dir ein Ziel.
5. Nicht alles optimieren
Ich testete auch Dinge, die nichts brachten:
- OLLAMA_NUM_PARALLEL=4: Verschlechterte Single-User-Performance
- Memory Clock Tuning: Kein messbarer Effekt
- OLLAMA_GPU_OVERHEAD anpassen: Kein Unterschied
Lektion: Wenn eine Optimierung keinen messbaren Effekt hat, lass es. Komplexität ohne Nutzen ist technische Schuld.
Die finale Konfiguration
Nach allen Optimierungen sieht die perfekte Ollama-Config so aus:
# /etc/systemd/system/ollama.service.d/override.conf
[Service]
Environment="LD_LIBRARY_PATH=/usr/local/lib/ollama/cuda_v13:/usr/local/lib/ollama/cuda_v12:/usr/lib/x86_64-linux-gnu"
Environment=OLLAMA_FLASH_ATTENTION=1
Environment=CUDA_VISIBLE_DEVICES=0
Environment=OLLAMA_HOST=0.0.0.0:11434
Environment=OLLAMA_CONTEXT_LENGTH=4096
# NICHT setzen (Auto-Detection ist besser):
# Environment=OLLAMA_LLM_LIBRARY=...
# Optional für Dauerlast:
# GPU Persistence Mode: nvidia-smi -pm 1
# GPU Clock Lock: nvidia-smi -lgc 1560
Was würde ich anders machen?
Wenn ich nochmal bei Null anfangen würde:
- Zuerst: Neueste Ollama-Version installieren (nicht die aus dem Package-Manager!)
- Dann: Flash Attention in der Config setzen
- Testen: Baseline-Benchmark fahren
- Nur bei Problemen: Debug-Logs aktivieren und systematisch prüfen
Das hätte mir die OLLAMA_LLM_LIBRARY-Odyssee erspart. Aus meiner Perspektive war das ein Learning: Weniger ist mehr. Die Defaults sind gut, manuelle Overrides oft kontraproduktiv.
Für wen ist diese Analyse relevant?
Wenn Du:
- Ollama auf einem Server mit NVIDIA-GPU betreibst
- Performance-Probleme hast (unter 50 t/s bei 20B Modellen)
- Nicht sicher bist, ob Deine Config optimal ist
- Systematisch optimieren willst statt wild zu probieren
Dann ist diese Checkliste für Dich. Arbeite sie durch, miss jeden Schritt, dokumentiere die Ergebnisse. Nach 30 Minuten weißt Du, wo Dein System steht.
Wenn Dich solche Themen interessieren, dann schau gerne mal vorbei: