Kapitel 7 — Fortgeschrittene Batching-Strategien
Siebter Beitrag im Kapitel-für-Kapitel-Walkthrough zu LLM Primer VI: KI-Systeme skalieren. Das Kapitel zeigt, dass Batching keine Optimierung ist, sondern der tragende Zug, der bandbreitengebundene Dekodierung überhaupt bezahlbar macht — und warum der Batch ein Verb ist, kein Substantiv.
Warum es dieses Kapitel gibt
Ein einzelner Dekodierschritt liest zig Gigabyte Gewichte aus dem HBM, um ein einziges Token zu erzeugen. Wenn die GPU die Gewichte ohnehin streamt, kann sie im selben Schritt für viele Anfragen ein Token erzeugen, praktisch ohne Grenzkosten. Batching ist deshalb keine Optimierung im gewöhnlichen Sinn — es ist der einzige Weg, Dekodierung auf speicherbandbreitengebundener Hardware wirtschaftlich zu machen. Die naheliegende Art zu batchen kollabiert allerdings bei Kontakt mit der realen Welt, weil Anfragen unterschiedliche Längen haben und zu unterschiedlichen Zeitpunkten fertig werden. Kapitel 7 geht vom naiven Schema, das fast funktioniert, bis zum iterationsweisen Scheduler, der in Produktions-Engines läuft, und benennt die Schulden, die dieser Scheduler für Kapitel 8 hinterlässt.
7.1 Statisches Batching scheitert am Problem des schnellsten Beenders
Statisches Batching ist das, was jeder zuerst schreibt: Anfragen sammeln, bis Batchgröße B oder ein Timeout erreicht ist, Prefill über den ganzen Batch auf Länge S_max laufen lassen, dann eine Dekodierschleife bis jede Sequenz im Batch EOS gemeldet hat. Zwei Kosten sind in Kombination katastrophal. Padding — der 50-Token-Prompt im selben Batch wie der 4.000-Token-Prompt bezahlt für die achtzigfache Prefill-Arbeit, die er nicht braucht. Schnellster Beender — die Dekodierschleife läuft, bis jede Sequenz fertig ist, sodass ein Batch mit einer 20-Token-Anfrage und einer 2.000-Token-Anfrage 99 Prozent seiner Zeit damit verbringt, dass 31 Slots Tokens produzieren, die verworfen werden. Der mittlere Durchsatz sieht akzeptabel aus, und die Tail-Latenz wird von der zufällig längsten Anfrage im Batch dominiert. Dynamisches Batching (variables Timeout, späte Ankünfte in den wartenden Batch zulassen) weicht das Warten auf, ändert aber nichts am grundsätzlichen Schema; sobald Dekodierung beginnt, erbt es dasselbe Problem.
7.2 Continuous Batching schedult pro Iteration, nicht pro Anfrage
Continuous Batching — Orcas iterationsweises Scheduling, NVIDIAs In-Flight-Batching, „der zentrale Trick" von vLLM und TGI — ist der Variablenwechsel. Statt den Batch für die Dauer der Erzeugung festzuschreiben, überdenkt der Scheduler die Batch-Zusammensetzung bei jedem Dekodierschritt. Nach jedem Schritt: fertige Sequenzen verlassen ihre Slots und geben ihren KV frei; neue Sequenzen aus der Warteschlange rücken in die freigewordenen Slots; der gemeinsame Dekodierschritt läuft über das neue aktive Set. Zwei Eigenschaften der LLM-Dekodierung machen das möglich. Jeder Dekodierschritt ist strukturell dieselbe Operation — dem Kernel ist egal, welche Sequenz in Slot 7 oder Slot 11 sitzt. Und jede Sequenz trägt ihren eigenen KV-Cache unabhängig, sodass ihr Speicher schlicht frei wird, wenn sie geht. Die Fairness-Einheit wird die Iteration, nicht die Anfrage: eine 20-Token-Anfrage ist nach etwa 20 Iterationen fertig, egal wer sonst im Batch ist, und eine 2.000-Token-Anfrage belegt einen Slot so lange, wie sie ihn braucht, ohne andere in Geiselhaft zu nehmen. Auf realem Long-Tail-Traffic — Mischungen aus kurzen Chat-Turns und langen RAG-Vervollständigungen — steigt die GPU-Auslastung auf einem 70B-Modell vom 10–20-Prozent-Bereich des statischen Batchings auf 60–80 Prozent, und die p99-Latenz zieht sich dramatisch an.
7.3 Chunked Prefill vereint Prefill und Dekodierung auf demselben Chip
Continuous Batching lässt eine Spannung zurück. Neuankömmlinge brauchen Prefill, was rechengebunden ist und lange Sequenzen pro Durchlauf bevorzugt. Laufende Sequenzen brauchen Dekodierung, was speicherbandbreitengebunden ist und breite Batches bevorzugt. Ein voller Prefill über einen frischen 4.000-Token-Prompt im selben Forward-Pass wie der Dekodierschritt für dreißig laufende Sequenzen verzögert entweder die laufenden Dekodierungen (schlechte TTFT für bestehende Nutzer) oder den Prefill des Neuankömmlings (schlechte TTFT für den neuen Nutzer). Chunked Prefill zerlegt den Prefill in Stücke — etwa 512 Tokens auf einmal — und verwebt diese Stücke mit den Dekodierschritten aktiver Sequenzen. Der einzelne Forward-Pass trägt nun gleichzeitig etwas Prefill-Arbeit für neue Prompts und etwas Dekodierarbeit für laufende, und beide Phasen teilen sich denselben Gewichtsstrom. Der rechengebundene Charakter des Prefills absorbiert die Arithmetik; der Bandbreitenbedarf der Dekodierung amortisiert sich über mehr nützliche Arbeit pro Byte. Auf einem einzelnen Chip hören die zwei Phasen auf, gegeneinander zu arbeiten. Der verbleibende Fall — genug Traffic, dass sie doch wieder streiten — ist das Setup für disaggregiertes Serving in Kapitel 12.
Was Kapitel 7 vorbereitet
Continuous Batching erledigt seine Aufgabe und stellt dabei den KV-Cache als bindende Beschränkung der Nebenläufigkeit bloß. Jede aktive Sequenz trägt ihren eigenen KV, dimensioniert nach ihrer aktuellen Länge; Sequenzen kommen und gehen in jedem Schritt; die Engine weiß im Voraus nicht, wie lang eine von ihnen wird. Ein naives Ein-Slab-pro-Slot-Layout gibt den Batching-Gewinn großenteils zurück. Kapitel 8 überträgt die Betriebssystemlösung: den Cache in kleine physische Blöcke zerlegen, sie von logischen Token-Positionen durch eine Seitentabelle entkoppeln und eine Eviction-Policy Blöcke zurückgewinnen oder zwischen Sequenzen teilen lassen. PagedAttention ist der Zug, der Continuous Batchings KV-Problem behandelbar macht.
Als Nächstes — Kapitel 8: KV-Cache-Management der nächsten Generation. PagedAttention, H2O-Eviction, InfiniGen und die Ökonomie des Präfix-Cachings.