vLLM, Ollama oder TGI: welche Inference Engine für welchen Fall
Die Engine entscheidet über Durchsatz, Latenz und Betriebsaufwand. Ein ehrlicher Vergleich der drei verbreitetsten Optionen, inklusive der Fälle, in denen die einfache Lösung gewinnt.
Die Schicht, an die niemand denkt
„Das Modell läuft, aber es ist langsam.“ Der Satz fällt fast immer, wenn ein Team sein erstes selbst gehostetes LLM produktiv setzt. Auf dem Laptop war alles flott, ein, zwei Nutzer, sofort eine Antwort. Dann gehen zehn Leute gleichzeitig drauf, und plötzlich dauert jede Antwort eine gefühlte Ewigkeit, während die teure GPU im Monitoring trotzdem halb gelangweilt vor sich hin dümpelt. Die Schuld liegt selten am Modell. Sie liegt an der Schicht darunter, an die beim Aufsetzen niemand gedacht hat: der Inference-Engine.
Diese Schicht nimmt Requests an, verwaltet den Speicher, plant die Berechnung und streamt die Tokens zurück. Sie entscheidet über fast alles, was du im Betrieb spürst: wie viele Nutzer gleichzeitig passen, wie lange das erste Token braucht, wie viel VRAM du wirklich brauchst und wie viel deiner Karte brachliegt. vLLM, Ollama und TGI lösen diese Aufgabe mit grundverschiedenen Prioritäten. Wer sie verwechselt, zahlt entweder mit schlechter Auslastung oder mit unnötiger Komplexität. Um die Wahl zu verstehen, muss man kurz darunterschauen, was beim Generieren eigentlich passiert.
Warum ein Einzelrequest deine GPU langweilt
Ein Request läuft in zwei Phasen ab, und die zweite ist das Problem. Im Prefill verarbeitet die GPU den ganzen Prompt auf einmal, parallel über alle Tokens, rechenintensiv, aber kurz, und die Karte ist gut ausgelastet. Im Decode entsteht die Antwort Token für Token, jeder Schritt hängt vom vorigen ab. Diese Phase ist seriell und speicher- statt rechengebunden: Die GPU wartet mehr auf Daten, als dass sie rechnet. Ein einzelner Decode-Schritt multipliziert einen kleinen Vektor mit den Gewichten und beschäftigt nur einen Bruchteil der zehntausenden Recheneinheiten einer modernen Karte.
Genau deshalb lastet ein einzelner Nutzer die GPU nie aus, egal wie schnell die Hardware ist. Der einzige Weg, sie zu füllen, ist, viele Requests gleichzeitig durch dieselben Gewichts-Matrizen zu schicken. Durchsatz ist damit keine Frage der rohen Rechenleistung, sondern der Frage, wie clever die Engine parallele Anfragen bündelt. Und das wiederum scheitert oft nicht an der Rechenzeit, sondern am Speicher, an einer Struktur namens KV-Cache.
Der KV-Cache frisst deinen Speicher
Damit das Modell beim Decode nicht in jedem Schritt den ganzen Kontext neu berechnet, speichert es pro Token Zwischenergebnisse: die Keys und Values jeder Attention-Schicht. Dieser KV-Cache wächst linear mit der Kontextlänge und mit der Zahl gleichzeitiger Nutzer, und er liegt im VRAM direkt neben den Modellgewichten. In der Praxis ist nicht das Modell der Engpass, sondern dieser Cache: Er bestimmt, wie viele Nutzer gleichzeitig passen, bevor dir der Speicher ausgeht.
Die Grössenordnung überrascht fast jeden, der sie zum ersten Mal rechnet. Grob gilt pro Sequenz KV-Bytes = 2 · n_layers · n_kv_heads · head_dim · seq_len · bytes_pro_element, wobei die 2 für Keys und Values steht. Nimm ein 8B-Modell mit Grouped-Query-Attention: 32 Layer, 8 KV-Heads, head_dim 128, fp16 mit 2 Byte. Pro Token sind das 2 · 32 · 8 · 128 = 65 536 Elemente, mal 2 Byte also 128 KiB. Bei 8 192 Token Kontext wächst das auf rund 1 GiB pro Sequenz, und bei 32 gleichzeitigen Nutzern mit vollem Kontext landest du bei 32 GiB allein für den KV-Cache, zusätzlich zu den rund 16 GiB Modellgewichten. Auf einer 80-GiB-Karte geht das knapp auf, aber du siehst sofort: Der Kontext, nicht das Modell, ist deine Obergrenze. Bei einem 70B-Modell mit 80 Layern liegt der Cache schnell bei 2,5 GiB pro Sequenz, und die Gewichte sprengen ohnehin eine einzelne GPU.
Hier liegt auch ein stiller Hebel: Wie die Gewichte lässt sich der KV-Cache quantisieren. Speicherst du Keys und Values in fp8 statt fp16, halbierst du den KV-Speicher und verdoppelst grob die mögliche Zahl gleichzeitiger Sequenzen, meist bei kaum messbarem Qualitätsverlust. Engines, die fp8-KV-Cache beherrschen, holen so aus derselben Karte deutlich mehr Concurrency, einer der Punkte, die im Engine-Vergleich oft den Ausschlag geben.
Batching ist der ganze Unterschied
Wie eine Engine mit dem Speicher und der Parallelität umgeht, trennt Spielzeug von Produktion. Beim statischen Batching sammelt die Engine eine feste Gruppe von Requests, verarbeitet sie gemeinsam und gibt erst frei, wenn der langsamste fertig ist. Ein Nutzer, der drei Tokens will, blockiert dann seinen Slot, bis der Nutzer mit dreitausend Tokens durch ist, und die GPU läuft halbleer. Continuous Batching räumt damit auf: Sobald eine Sequenz fertig ist, rückt im nächsten Schritt sofort eine neue nach. Die GPU bleibt voll, die Wartezeiten sinken, und dieser eine Unterschied erklärt einen Grossteil der Durchsatzlücke zwischen den Engines.
vLLM geht noch einen Schritt weiter und löst das Speicherproblem an der Wurzel. Klassisch reserviert man pro Sequenz einen zusammenhängenden KV-Block für die maximal mögliche Länge und verschwendet alles, was der Nutzer nicht ausschöpft. PagedAttention zerlegt den KV-Cache stattdessen in kleine, gleich grosse Blöcke und verwaltet sie wie ein Betriebssystem virtuellen Speicher: nicht zusammenhängend, bei Bedarf zugeteilt, kaum Fragmentierung. Dadurch passen spürbar mehr Sequenzen in denselben VRAM. Geteilte Präfixe, etwa ein gemeinsamer System-Prompt, lassen sich über Prefix Caching sogar mehrfach nutzen, statt sie pro Request neu zu berechnen. Höhere Belegung heisst direkt höherer Durchsatz pro Franken GPU.
Drei Engines, drei Prioritäten
vLLM ist die Engine für den produktiven Ernstfall. PagedAttention und Continuous Batching sind der Kern, dazu kommen Tensor-Parallelismus, um grosse Modelle über mehrere GPUs zu legen, Prefix Caching, optional speculative decoding für niedrigere Latenz und eine OpenAI-kompatible API, gegen die deine bestehende Client-Bibliothek einfach läuft. In unseren Tests liefert vLLM bei vielen parallelen Nutzern typischerweise ein Vielfaches der Tokens pro Sekunde gegenüber einer llama.cpp-basierten Engine. Die genaue Zahl hängt an Modell, Kontext und GPU, aber die Grössenordnung ist konsistent. Der Preis ist Komplexität: vLLM will konfiguriert werden und läuft am besten auf sauberer Datacenter-GPU wie einer H100 bare metal oder einer DGX Spark.
Ollama steht am anderen Ende. Ein Befehl, und ein Modell läuft, auf CPU, auf Apple Silicon, auf einer kleinen GPU. Im Kern steckt llama.cpp mit GGUF-Modellen, und das Modell-Hot-Swapping macht das Ausprobieren angenehm. Für lokale Entwicklung, Prototypen und Einzelnutzer ist das unschlagbar bequem. Unter echter Parallel-Last fällt Ollama aber deutlich zurück: Das Concurrency-Batching ist schwach, der Durchsatz pro GPU niedrig. Für einen Endpoint mit vielen gleichzeitigen Nutzern ist es das falsche Werkzeug, so angenehm es im Kleinen ist.
Dazwischen sitzt TGI, Hugging Faces produktionsreife Engine. Sie bringt Continuous Batching, Token-Streaming und fertige Container mit und ist eng ins Hugging-Face-Ökosystem integriert. Wenn dein Stack ohnehin dort lebt und dir gute Integration und robuste Defaults wichtiger sind als die letzten Prozent Durchsatz, ist TGI eine bequeme, solide Wahl. Am absoluten oberen Ende des Durchsatzes hat vLLM in vielen Szenarien dennoch die Nase vorn, und der direkte Vergleich auf deinem Modell lohnt sich.
Die Entscheidung nach Lastprofil
Die Wahl folgt nicht der Mode, sondern deiner Last und deiner Hardware. In den allermeisten Fällen läuft sie auf eine dieser drei Situationen hinaus:
- Einzelnutzer oder Prototyp: Du entwickelst lokal, baust eine interne Demo oder testest Modelle. Nimm Ollama, denn Apple Silicon oder eine kleine Karte reichen, du bist in Minuten startklar, und schwaches Batching ist bei einem Nutzer egal.
- Mittlere Last mit HF-Bindung: Du gehst produktiv, hast moderate Parallelität und steckst tief im Hugging-Face-Stack. TGI gibt dir Continuous Batching und fertige Container ohne grossen Tuning-Aufwand, der pragmatische Default.
- Hohe Parallel-Last: Viele gleichzeitige Nutzer, hoher Token-Durchsatz, Datacenter-GPU. vLLM, weil PagedAttention, Tensor-Parallelismus und fp8-KV-Cache die Komplexität rechtfertigen: Sie tragen pro GPU am meisten Last und drücken damit die Kosten pro Token.
Die Schwelle, ab der sich der Umstieg von Ollama auf vLLM lohnt, liegt nicht bei einer bestimmten Nutzerzahl, sondern dort, wo aus „ein paar Leute probieren das mal aus“ ein echter, gleichzeitig genutzter Endpoint wird. Spätestens dann zahlt sich die Engine, die deine GPU wirklich füllt, in barer Münze aus.
Wer die Engine am Ende betreibt
Die Engine auszuwählen ist der einfache Teil. Sie aktuell zu halten, für ein neues Modell neu zu tunen, fp8 und Parallelismus richtig zu setzen und unter Last stabil zu fahren, ist der Aufwand, der bleibt, und genau der Betriebsblock, den wir in den Kosten eigener LLMs auseinandergenommen haben. Wenn du die Datenhoheit offener Modelle willst, aber nicht den Betrieb erben möchtest, übernehmen wir mit Managed Inference genau diese Schicht und wählen die Engine passend zu deinem Workload, statt dich auf eine festzunageln.
Weitere Artikel
- DGX Spark gegen Cloud-GPU: wann sich eigene Hardware lohnt
- Braucht ihr wirklich einen Agent oder reicht ein Endpoint?
- Was Datenhoheit konkret heisst und wo deine Prompts wirklich landen
- DGX Spark im Betrieb: was die Maschine nach drei Monaten wirklich kann
- Eigenen LLM Endpoint aufsetzen: von der GPU zum ersten Token
- Welches offene Modell für welche Aufgabe? Llama, Qwen, Mistral & Co.