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?

Zum Inhalt

Inhalt

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:

Das Test-Modell

gpt-oss:20b als Benchmark-Modell:

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:

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?

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:

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.

Zur Diagnose-Checkliste

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:

  1. Ollama-Version prüfen: Alles unter 0.12.x ist veraltet
  2. Flash Attention aktiviert? Ohne geht es deutlich langsamer
  3. Wird die GPU genutzt? Oder läuft es auf der CPU?
  4. Context Length sinnvoll? Höher ist nicht immer besser
  5. 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:

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:

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:

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:

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:

# 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:

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:

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:

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:

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 LLM-Library-Falle

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:

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:

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:

  1. "discovering available GPUs": GPU-Discovery startet
  2. "skipping cuda_v12": Erste Library wird übersprungen
  3. "skipping cuda_v13": Zweite Library wird übersprungen
  4. "count=0": Keine GPU-Libraries verfügbar
  5. "cpu library": Fallback auf CPU
  6. "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:

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: Version-Upgrade

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:

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):

Version 0.12.0 (Januar 2025):

Version 0.12.6 (Oktober 2025, aktuell):

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

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:

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

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.

GPU-Optimierungen

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:

Ich lasse es aktiv, weil der Server durchgehend Inference-Requests bedient.

Performance-Matrix

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:

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

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

  1. Version prüfen: ollama --version (sollte 0.12.6 Plus sein)
  2. Flash Attention: grep FLASH_ATTENTION /etc/systemd/system/ollama.service.d/override.conf (sollte =1 sein)
  3. GPU-Nutzung: nvidia-smi während Run (sollte 10 Plus GB VRAM zeigen)
  4. OLLAMA_LLM_LIBRARY: Sollte NICHT gesetzt sein (Auto-Detection ist besser)
  5. 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:

  1. Baseline messen (ohne Änderungen)
  2. Eine Änderung machen
  3. Neu messen
  4. Änderung behalten oder verwerfen
  5. 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:

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:

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:

  1. Zuerst: Neueste Ollama-Version installieren (nicht die aus dem Package-Manager!)
  2. Dann: Flash Attention in der Config setzen
  3. Testen: Baseline-Benchmark fahren
  4. 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:

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:

KI systemisch im Unternehmen einsetzen