Lokale LLM-Inferenz hat sich auf drei Stacks eingespielt, die ständig verwechselt werden: llama.cpp, Ollama und vLLM. Sie sind keine Konkurrenten auf derselben Ebene – zwei von ihnen umhüllen bzw. ergänzen einander direkt. Dieser Guide legt fest, was jedes Einzelne ist, wo sich Concurrency- und Speichermodelle unterscheiden und was man pro Szenario wählt.
Was jede Schicht wirklich ist
llama.cpp ist die C/C++-Inferenz-Engine: eine GGUF-native Runtime mit Backends für CUDA, Metal, Vulkan, ROCm und reine CPU, plus llama-server, ein eingebauter HTTP-Server mit OpenAI- und Anthropic-kompatiblen Routen, Continuous Batching und parallelem Decoding.1
Ollama ist eine Modell-Distributions- und Runtime-Schicht, keine eigene Engine (jedenfalls überwiegend). Historisch hat es einen gepinnten llama.cpp-Build umhüllt – die Variable LLAMA_CPP_VERSION im Ollama-Repo fixiert die exakte Upstream-Quelle, gepatcht über llama/compat/.2 Über diesen Pin hinaus hat Ollama eigene Engines ergänzt: eine Multimodal-Engine, die Vision-Modelle (Llama 4, Gemma 3, Qwen 2.5 VL) direkt auf der GGML-Tensor-Bibliothek ausführt, statt separaten Text-Decoder- und Vision-Encoder-Prozess zusammenzukleben,3 sowie eine MLX-basierte Engine für Apple Silicon, die Ollama im März 2026 in den Preview gestellt hat und für beschleunigte Modelle den llama.cpp-Metal-Pfad ersetzt.4
vLLM ist eine Python/CUDA-Serving-Engine rund um PagedAttention: KV-Cache-Speicher, OS-artig in Blöcken fester Größe verwaltet mit logisch-zu-physisch Block-Tabellen – dadurch nahezu Null Cache-Verschwendung und 2–4× Throughput gegenüber den Systemen aus dem ursprünglichen Paper.5 Seit der V1-Re-Architektur ist Prefix Caching standardmäßig aktiviert (unter 1% Durchsatzverlust bei 0% Hit-Rate), und V1 erreicht bis zu 1,7× höheren Durchsatz als V0.6
| Layer | llama.cpp | Ollama | vLLM |
|---|---|---|---|
| Was es ist | C/C++-GGUF-Engine + llama-server HTTP | Runtime + Modell-Registry um gepinnte llama.cpp-Version, eigene Multimodal-Engine, MLX-Engine auf Apple Silicon | Python/CUDA-Serving-Engine, PagedAttention-KV-Manager |
| Modellformat | GGUF (nativ) | GGUF (+ MLX-nativ auf Apple Silicon) | HF-Safetensors (GGUF über experimentelles Plugin) |
| Hauptziel | Eine Maschine, jede Hardware inkl. CPU | Ease of use, ollama run | GPU-Server-Durchsatz |
| Concurrency | Feste Slots (--parallel N) | OLLAMA_NUM_PARALLEL-Slots | Continuous Batching über einen gemeinsam genutzten KV-Pool |
Concurrency-Modelle: feste Slots vs. ein gemeinsamer Pool
Das ist der größte Verhaltensunterschied überhaupt – und die Quelle der übelsten Falle.
llama-server: --parallel N feste Slots, --ctx-size wird aufgeteilt
llama-server -np N erzeugt N Slots. Aktuelle Master-Defaults: -np/--parallel default -1 (auto), und -cb/--cont-batching (Continuous Batching) ist standardmäßig aktiviert; -b/--batch-size hat default 2048, -ub/--ubatch-size 512. --ctx-size (default 0 = aus dem Modell geladen) ist der Gesamt-Kontext; das KV-Budget wird auf die Slots aufgeteilt.1
Die Falle: --ctx-size ist die Summe, nicht pro Slot. Mit -c 8192 -np 4 erhält jeder Slot 8192/4 = 2048 Token KV-Platz – der 8k-Prompt wird still abgeschnitten oder erzeugt einen Fehler. Wer wirklich 8k pro Slot über 4 Slots will, muss -c 32768 -np 4 übergeben. Das hat genug Benutzer getroffen, dass Upstream nach Issue-Diskussion schließlich eine eigene Steuerung ergänzt hat (ctx-per-slot / --kv-unified-per-slot auf aktuellem Master), um das Kontext-Limit pro Slot direkt zu setzen.7
vLLM: ein gepagter KV-Pool, Continuous Batching
vLLM hält einen einzigen, gemeinsam genutzten KV-Pool und alloziert Blöcke on demand; Requests verlassen und ergänzen den Batch bei jedem Decode-Schritt. Höhere Concurrency kostet im Leerlauf nichts, weil kein per-Request-Kontext vorreserviert wird: Der KV-Cache eines Requests wächst Token für Token in physischen Blöcken und wird bei Request-Ende sofort freigegeben.5 --max-num-seqs begrenzt die Batchgröße; die eigentliche Grenze für parallele Requests ist, wie viele Sequenzen mit ihrem angewachsenen KV in den Pool passen.
Ollama: Slots wie llama.cpp, dimensioniert über Defaults
Ollama erbt das Slot-Modell: OLLAMA_NUM_PARALLEL (default 1) ist die Zahl paralleler Requests pro Modell, und der RAM-Bedarf skaliert mit OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH.8 Beim Default von 1 reihen sich gleichzeitige Requests ein; jeder zusätzliche Slot multipliziert die KV-Allokation genauso wie --parallel in llama-server.
GGUF: natives Zuhause vs. experimenteller Gast
GGUF ist llama.cpps natives Format – die Referenzimplementierung mit der kompletten Quant-Familie (Q2_K bis Q8_0, i-Quants, Imatrix-Kalibrierung).9
vLLMs GGUF-Support ist ein anderes Kapitel, in den Worten der eigenen Doku: "GGUF support in vLLM is highly experimental and under-optimized at the moment, it might be incompatible with other features." Das GGUF-Loading ist aus dem Kern in das OOT-Paket vllm-gguf-plugin gewandert (uv pip install vllm-gguf-plugin), nur Single-File-GGUFs werden unterstützt, und die Doku empfiehlt, den Tokenizer des Basismodells explizit mitzugeben, z. B.10:
vllm serve unsloth/Qwen3-0.6B-GGUF:Q4_K_M --tokenizer Qwen/Qwen3-0.6BPraktische Regel: GGUF ist in vLLM eine Speicherplatz-Reduktion, kein Performance-Pfad. Für hohen Serving-Durchsatz Safetensors mit einem erstklassigen Quantisierungsschema (AWQ, GPTQ, FP8) nutzen; GGUF nur, wenn das Modell nur als GGUF existiert.
Defaults, die beißen
Ollamas Kontext-Default ist VRAM-gestaffelt. Aktuelle Doku: < 24 GiB VRAM → 4k Kontext; 24–48 GiB → 32k; ≥ 48 GiB → 256k, überschreibbar mit OLLAMA_CONTEXT_LENGTH (ältere Releases hatten einen flachen 4096er-Default, und 4096 taucht noch in den Override-Beispielen der FAQ auf).11 Wenn die 24-GB-Karte einen 32k-Default wählt, den man nicht bestellt hat, wächst der KV-Cache entsprechend – ollama ps prüfen (Spalte CONTEXT) und num_ctx pro Request oder die Env-Variable global setzen.
Ollamas KV-Cache-Typ ist default f16. OLLAMA_KV_CACHE_TYPE akzeptiert f16 (default), q8_0 (~halber KV-Speicher, minimale Qualitätsminderung) oder q4_0 (~Viertel, deutlicher spürbar) – und wirkt nur, wenn Flash Attention aktiviert ist.8
OLLAMA_NUM_PARALLEL ist default 1 – gleichzeitige Requests laufen nicht parallel, sondern werden gereiht; Erhöhen multipliziert den KV-Speicher mit N.8
vLLMs gpu_memory_utilization ist default 0,9 – vLLM beansprucht beim Start bis zu 90% des Gesamt-Speichers der Karte (pro Instanz, nicht prozess-koordiniert) und zieht danach Gewichte, Aktivierungs-Peak und CUDA-Graph-Pools ab; der KV-Cache ist der Rest. Auf einer 24-GB-Karte mit Display oder anderen Prozessen schlägt 0,9 beim Start als OOM zu – senken (0,80–0,85) oder --max-model-len pinnen.12
Durchgerechnet: 14B Q4_K_M, 4 Slots × 8k, auf einer 24-GB-Karte
Nehmen wir Qwen2.5-14B (48 Layer, GQA mit 8 KV-Heads, Hidden Size 5120 über 40 Heads → Head-Dim 128, 14,7 Mrd. Parameter gesamt).13 Wir rechnen das Budget mit der KV-Formel dieser Seite (siehe KV-Cache-Guide und Inferenz-Mathematik) und setzen llama.cpps gemessene Q4_K_M-≈4,85 Bits pro Gewicht für diese Gewichtsklasse ein.9
Gewichte bei 4,85 bpw:
KV-Cache pro Token (f16, 2 Byte/Wert), mit :
Pro Slot bei 8.192 Tokens, und gesamt für 4 Slots:
| Komponente | Größe |
|---|---|
| Gewichte (Q4_K_M, 4,85 bpw) | 8,30 GiB |
| KV-Cache, 4 × 8k Slots, f16 | 6,00 GiB |
| Zwischensumme | 14,30 GiB |
| Puffer auf einer 24-GB-Karte (24 dezimale GB − die 14,30-GiB-Zwischensumme = 15,35 GB) | ~8,65 GB |
Es passt – mit deutlichem Puffer, und genau das ist die Aussage: Auf einer 24-GB-Karte liegen ein 14B-Modell in Q4_K_M und 4 parallele 8k-Konversationen komfortabel im Budget; die ~8,65 GB Puffer — 24 dezimale GB auf dem Aufkleber minus der 14,30-GiB-Zwischensumme (15,35 dezimale GB) — decken Compute-Scratch (abhängig von Batch-Größe und Backend) plus CUDA-Kontext. Zwei Hebel, wenn mehr Slots oder Kontext nötig sind: -ctk q8_0 -ctv q8_0 halbiert den KV-Cache auf 3,0 GiB (llama-server-Flags, f16 default1) – derselbe Trade, den Ollamas OLLAMA_KV_CACHE_TYPE=q8_0 macht. Für den vollständigen Sizing-Workflow siehe VRAM-Rechner erklärt und Quantisierung erklärt; das interaktive Tool ist der LLM-Inferenz-Rechner.
Und die Falle von oben gilt weiter: Für wirklich 8k pro Slot startet man llama-server -m model-Q4_K_M.gguf -c 32768 -np 4, nicht -c 8192.
Entscheidungstabelle
| Szenario | Wahl | Warum |
|---|---|---|
| Laptop / Desktop, ein Nutzer gleichzeitig | Ollama | Ein Befehl bis zum laufenden Modell; VRAM-gestaffelte Defaults, Modell-Registry, automatisches GPU-Splitting via darunterliegendem llama.cpp |
| Mac mit Apple Silicon, schnellstes Decode | Ollama (MLX-Engine) | Der MLX-Pfad ist für Unified Memory gebaut; Preview erfordert >32 GB Unified Memory und beschleunigt aktuell spezifische Modelle4 |
| Maximale Kontrolle: KV-Quantisierung, Slot-Tuning, exotische Hardware, CPU-Inferenz | llama.cpp (llama-server) | Direkter Zugriff auf jedes Flag (-ctk, -np, -c, --override-tensor), GGUF-nativ, läuft auf CPU – vLLM-GGUF tut das nicht |
| Viele gleichzeitige Nutzer, eine oder viele GPUs, Latenz-SLO | vLLM | Gepagerter KV-Pool + Continuous Batching skalieren mit Last; Prefix Caching standardmäßig an6 |
| Produktions-API mit strukturierten Ausgaben / Tool-Use bei hohem QPS | vLLM | Erstklassige Serving-Features, Tensor-Parallelität über GPUs, FP8/AWQ-Quantisierung |
| Multimodal (Vision) lokal | Ollama oder llama-server | Ollamas Multimodal-Engine macht Vision zur first-class citizen3; llama-servers --mmproj deckt GGUF-Vision-Modelle ab1 |
| Ein HF-only-Modell als GGUF servieren | llama.cpp oder Ollama | vLLMs GGUF-Pfad ist 'highly experimental', nur Single-File, Plugin-basiert10 |
Faustregel: llama.cpp, wenn man an die Regler muss oder exotische Hardware bedienen will; Ollama für reibungslose lokale Nutzung; vLLM, wenn andere Menschen oder Services auf den Endpunkt zugreifen und Durchsatz pro GPU zählt.
Footnotes
-
ggml-org/llama.cpp, tools/server/README.md (master): https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md —
-np/--paralleldefault -1 (auto),-cb(Continuous Batching) default aktiviert,-bdefault 2048,-ubdefault 512,-c/--ctx-sizedefault 0 (aus dem Modell geladen). ↩ ↩2 ↩3 ↩4 -
ollama/ollama, llama/README.md:
LLAMA_CPP_VERSIONpinnt Ollamas llama.cpp-Quelle: https://github.com/ollama/ollama/blob/main/llama/README.md ↩ -
Ollama-Blog, „Ollama's new engine for multimodal models" (15. Mai 2025): https://ollama.com/blog/multimodal-models ↩ ↩2
-
Ollama-Blog, „Ollama is now powered by MLX on Apple Silicon in preview" (März 2026): https://ollama.com/blog/mlx ↩ ↩2
-
Kwon et al., „Efficient Memory Management for Large Language Model Serving with PagedAttention", SOSP 2023, arXiv:2309.06180: https://arxiv.org/abs/2309.06180 ↩ ↩2
-
vLLM-Blog, „vLLM V1: A Major Upgrade to vLLM's Core Architecture" (2025-01-27): bis zu 1,7× Throughput vs. V0; Prefix Caching default mit \<1% Durchsatzverlust bei 0% Hit-Rate: https://blog.vllm.ai/2025/01/27/v1-alpha-release.html ↩ ↩2
-
llama.cpp Issue #11681 („--ctx-size is divided by --parallel") und PR #24124 (Commit 1844325) / Release b10662 (
ctx-per-slot,--kv-unified-per-slot): https://github.com/ggml-org/llama.cpp/issues/11681 ↩ -
Ollama-FAQ:
OLLAMA_NUM_PARALLELdefault 1, RAM skaliert mitOLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH;OLLAMA_KV_CACHE_TYPEdefault f16 mit q8_0/q4_0-Optionen: https://docs.ollama.com/faq ↩ ↩2 ↩3 -
ggml-org/llama.cpp, tools/quantize/README.md: Q4_K_M = 4,8944 bpw an Llama-3.1-8B (≈4,85–4,9 bpw Gewichtsklasse): https://github.com/ggml-org/llama.cpp/blob/master/tools/quantize/README.md ↩ ↩2
-
vLLM-Doku, GGUF-Feature-Seite: „highly experimental and under-optimized", Migration zum OOT vllm-gguf-plugin, nur Single-File-GGUF, Basismodell-Tokenizer empfohlen: https://github.com/vllm-project/vllm/blob/main/docs/features/quantization/gguf.md ↩ ↩2
-
Ollama-Doku, Context length: <24 GiB VRAM → 4k, 24–48 GiB → 32k, ≥48 GiB → 256k,
OLLAMA_CONTEXT_LENGTH-Override: https://docs.ollama.com/context-length ; älterer flacher 4096-Default gemäß https://docs.ollama.com/faq ↩ -
vLLM EngineArgs:
gpu_memory_utilizationdefault 0,9, „Fraction of GPU memory to be used for the model executor", pro Instanz: https://docs.vllm.ai/en/latest/api/engine_args.html (vllm/config/cache.py, default=0.9) ↩ -
Qwen2.5-14B-Instruct-Model Card (48 Layer, GQA 40 Q- / 8 KV-Heads, 14,7 Mrd. Parameter) und config.json (hidden_size 5120, num_key_value_heads 8): https://huggingface.co/Qwen/Qwen2.5-14B-Instruct ↩