Zum Inhalt springen
Kurzer Hinweis: flozi00 TechHub ist ein Solo-Nebenprojekt neben einem Vollzeitjob — persönliche Lernnotizen, keine offiziellen Aussagen. Kritische Schritte selbst prüfen.

llama.cpp / Ollama vs vLLM: Was, wann und warum

Ein Zahlen-first-Vergleich der drei dominierenden lokalen LLM-Inferenz-Stacks: Was jede Schicht wirklich ist, wie sich ihre Concurrency-Modelle unterscheiden, die GGUF-Frage und eine durchgerechnete VRAM-Budgetierung für ein 14B-Modell in Q4_K_M auf einer 24-GB-Karte.

7 Min. Lesezeitflozi00
aimachine-learninggpullama-cppollamavllm

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

Layerllama.cppOllamavLLM
Was es istC/C++-GGUF-Engine + llama-server HTTPRuntime + Modell-Registry um gepinnte llama.cpp-Version, eigene Multimodal-Engine, MLX-Engine auf Apple SiliconPython/CUDA-Serving-Engine, PagedAttention-KV-Manager
ModellformatGGUF (nativ)GGUF (+ MLX-nativ auf Apple Silicon)HF-Safetensors (GGUF über experimentelles Plugin)
HauptzielEine Maschine, jede Hardware inkl. CPUEase of use, ollama runGPU-Server-Durchsatz
ConcurrencyFeste Slots (--parallel N)OLLAMA_NUM_PARALLEL-SlotsContinuous 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:

sh
vllm serve unsloth/Qwen3-0.6B-GGUF:Q4_K_M --tokenizer Qwen/Qwen3-0.6B

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

W=14,7×109×4,858=8,912 GB≈8,30 GiBW = \frac{14{,}7 \times 10^9 \times 4{,}85}{8} = 8{,}912 \,\text{GB} \approx 8{,}30 \,\text{GiB}

KV-Cache pro Token (f16, 2 Byte/Wert), mit 2×L×HKV×dhead×bdtype×s2 \times L \times H_{KV} \times d_{head} \times b_{dtype} \times s:

k=2×48×8×128×2=196,608 B/Tokenk = 2 \times 48 \times 8 \times 128 \times 2 = 196{,}608 \,\text{B/Token}

Pro Slot bei 8.192 Tokens, und gesamt für 4 Slots:

KVSlot=8192×196,608=1,611 GB=1,5 GiBKV_{Slot} = 8192 \times 196{,}608 = 1{,}611 \,\text{GB} = 1{,}5 \,\text{GiB} KVgesamt=4×1,5 GiB=6,0 GiBKV_{gesamt} = 4 \times 1{,}5 \,\text{GiB} = 6{,}0 \,\text{GiB}
KomponenteGröße
Gewichte (Q4_K_M, 4,85 bpw)8,30 GiB
KV-Cache, 4 × 8k Slots, f166,00 GiB
Zwischensumme14,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

SzenarioWahlWarum
Laptop / Desktop, ein Nutzer gleichzeitigOllamaEin Befehl bis zum laufenden Modell; VRAM-gestaffelte Defaults, Modell-Registry, automatisches GPU-Splitting via darunterliegendem llama.cpp
Mac mit Apple Silicon, schnellstes DecodeOllama (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-Inferenzllama.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-SLOvLLMGepagerter KV-Pool + Continuous Batching skalieren mit Last; Prefix Caching standardmäßig an6
Produktions-API mit strukturierten Ausgaben / Tool-Use bei hohem QPSvLLMErstklassige Serving-Features, Tensor-Parallelität über GPUs, FP8/AWQ-Quantisierung
Multimodal (Vision) lokalOllama oder llama-serverOllamas Multimodal-Engine macht Vision zur first-class citizen3; llama-servers --mmproj deckt GGUF-Vision-Modelle ab1
Ein HF-only-Modell als GGUF servierenllama.cpp oder OllamavLLMs 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

  1. ggml-org/llama.cpp, tools/server/README.md (master): https://github.com/ggml-org/llama.cpp/blob/master/tools/server/README.md — -np/--parallel default -1 (auto), -cb (Continuous Batching) default aktiviert, -b default 2048, -ub default 512, -c/--ctx-size default 0 (aus dem Modell geladen). ↩ ↩2 ↩3 ↩4

  2. ollama/ollama, llama/README.md: LLAMA_CPP_VERSION pinnt Ollamas llama.cpp-Quelle: https://github.com/ollama/ollama/blob/main/llama/README.md ↩

  3. Ollama-Blog, „Ollama's new engine for multimodal models" (15. Mai 2025): https://ollama.com/blog/multimodal-models ↩ ↩2

  4. Ollama-Blog, „Ollama is now powered by MLX on Apple Silicon in preview" (März 2026): https://ollama.com/blog/mlx ↩ ↩2

  5. 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

  6. 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

  7. 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 ↩

  8. Ollama-FAQ: OLLAMA_NUM_PARALLEL default 1, RAM skaliert mit OLLAMA_NUM_PARALLEL * OLLAMA_CONTEXT_LENGTH; OLLAMA_KV_CACHE_TYPE default f16 mit q8_0/q4_0-Optionen: https://docs.ollama.com/faq ↩ ↩2 ↩3

  9. 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

  10. 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

  11. 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 ↩

  12. vLLM EngineArgs: gpu_memory_utilization default 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) ↩

  13. 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 ↩