Chunked Prefill
Chunked Prefill teilt lange Prompts in Token-Blöcke auf, die abwechselnd mit der Dekodierung anderer Anfragen verarbeitet werden, damit ein einzelner langer Prompt nicht die übrige Warteschlange blockiert.
Chunked Prefill ist eine Modellbereitstellung-Technik, bei der eine lange Eingabeaufforderung in kleinere Gruppen von Tokens aufgeteilt und über mehrere Schritte hinweg verarbeitet wird, gemischt mit der Tokenerzeugung für andere Anfragen. Ohne diese Technik verbringt eine Inferenz-Engine, die eine lange Eingabeaufforderung annimmt, einen ganzen Schritt mit dieser Eingabe. Alle Antworten, die bereits für andere Benutzer gestreamt werden, pausieren, bis die Verarbeitung abgeschlossen ist. Die Arbeit SARATHI von 2023 führte Chunked Prefill ein. Der Nachfolger Sarathi-Serve entwickelte darauf aufbauend eine „störungsfreie“ Ablaufplanung. Dynamic SplitFuse von DeepSpeed-FastGen verwendet dieselbe Idee. Chunked Prefill ist inzwischen standardmäßig in vLLM aktiviert und in SGLang sowie TensorRT-LLM verfügbar – Engines, die viele quelloffene große Sprachmodelle (LLMs) bereitstellen.
Prefill, Decode und Verzögerungen bei der Generierung#
Die Inferenz mit Transformern nach dem autoregressiven Verfahren umfasst zwei Phasen:
- Prefill: Das Modell verarbeitet alle Tokens der Eingabeaufforderung parallel, berechnet ihre Schlüssel und Werte und speichert sie im KV-Cache. Beim Prefill werden viele Operationen pro gelesenem Byte an Gewichten ausgeführt, weshalb es üblicherweise rechengebunden ist.
- Decode: Das Modell erzeugt für jede Anfrage pro Schritt ein Token und liest dabei jedes Mal den gesamten Cache. Beim Decode gibt es nur wenige Rechenoperationen pro gelesenem Byte, weshalb der Vorgang üblicherweise durch die Speicherbandbreite begrenzt ist.
Ein GPU-Schritt mit einem Prefill von 20.000 Tokens dauert deutlich länger als ein Schritt mit Decodes, sodass jede Anfrage im Stapel darauf warten muss. Sarathi-Serve bezeichnet diese Pausen als Verzögerungen bei der Generierung. Sie zeigen sich als Spitzen bei der Latenz zwischen Tokens für Benutzer, deren eigene Eingabeaufforderungen kurz sind.
So funktioniert Chunked Prefill#
Die Ablaufplanung weist jedem Schritt ein Tokenbudget zu, das in vLLM durch max_num_batched_tokens festgelegt wird. Zuerst fügt sie für jeden laufenden Decode ein Token hinzu und füllt dann das verbleibende Budget mit Abschnitten wartender Eingabeaufforderungen. Passt eine Eingabeaufforderung nicht vollständig hinein, wird sie im nächsten Schritt fortgesetzt. Ihr KV-Cache wird Abschnitt für Abschnitt erweitert, bis die gesamte Eingabe verarbeitet ist und der Decode beginnt. Werden beide Phasen in einem Schritt kombiniert, wird auch die GPU besser ausgelastet: Beim Decode bleibt Rechenkapazität ungenutzt, die durch Prefill-Abschnitte gefüllt werden kann:
Chunking verändert nur den Zeitpunkt, zu dem die Tokens der Eingabeaufforderung verarbeitet werden. Jeder Abschnitt berücksichtigt mithilfe einer kausalen Maske die zwischengespeicherten Schlüssel und Werte aller vorhergehenden Abschnitte sowie seine eigenen Tokens. Das Ergebnis entspricht daher einem Prefill in einem Durchgang, wie eine Aufmerksamkeitsschicht in PyTorch zeigt:
import torch
import torch.nn.functional as F
torch.manual_seed(0)
prompt_len, chunk = 1000, 256
q, k, v = torch.randn(3, 1, 8, prompt_len, 64).unbind(0) # one layer: 8 heads, 64 dims each
# One-shot prefill: all prompt tokens attend causally in a single pass
full = F.scaled_dot_product_attention(q, k, v, is_causal=True)
# Chunked prefill: each chunk attends to the cached keys and values of earlier chunks plus itself
outputs = []
for start in range(0, prompt_len, chunk):
end = min(start + chunk, prompt_len)
mask = torch.ones(end - start, end, dtype=torch.bool).tril(diagonal=start) # causal over absolute positions
q_chunk, k_cached, v_cached = q[..., start:end, :], k[..., :end, :], v[..., :end, :]
outputs.append(F.scaled_dot_product_attention(q_chunk, k_cached, v_cached, attn_mask=mask))
chunked = torch.cat(outputs, dim=2)
print(f"{len(outputs)} chunks, same result as one pass: {torch.allclose(full, chunked, atol=1e-5)}")Die Ausgabe lautet 4 chunks, same result as one pass: True: Die Eingabeaufforderung mit 1.000 Tokens wird in Abschnitten mit 256, 256, 256 und 232 Tokens verarbeitet. Die Ausgaben stimmen mit dem Durchgang in einem Schritt überein.
Verwandte Techniken zur Bereitstellung#
- Kontinuierliche Stapelverarbeitung: Fügt bei jedem Schritt Anfragen hinzu und entfernt sie, statt auf den Abschluss eines ganzen Stapels zu warten. Chunked Prefill ermöglicht es, lange Eingabeaufforderungen innerhalb dieser Schleife einzuplanen.
- PagedAttention: Speichert den KV-Cache in Seiten fester Größe, sodass der Cache einer über mehrere Schritte verarbeiteten Eingabeaufforderung wachsen kann, ohne dass von Anfang an Speicherplatz für ihre gesamte Länge reserviert werden muss.
- Zwischenspeicherung von Eingabeaufforderungen: Verwendet die zwischengespeicherten Schlüssel und Werte eines wiederholten Präfixes erneut und lässt dadurch den entsprechenden Teil des Prefill vollständig entfallen.
- Getrenntes Prefill und Decode: Führt die beiden Phasen auf getrennten GPU-Pools aus und überträgt den KV-Cache zwischen ihnen. So kann jeder Pool entsprechend seinem eigenen Engpass skaliert und abgestimmt werden.
- Spekulatives Decoding: Beschleunigt die Decode-Phase, indem mehrere Tokens vorläufig erzeugt und in einem Durchgang überprüft werden.
Anwendungen#
Chunked Prefill ist besonders wichtig, wenn sich lange und kurze Eingabeaufforderungen einen Server teilen. Ein Assistent für Retrieval-Augmented Generation (RAG), der sein Kontextfenster mit einem 50-seitigen Bericht füllt, kann diesen in Abschnitten vorverarbeiten, während die Chats anderer Benutzer weiter gestreamt werden. Vision-Sprachmodelle (VLMs) fügen jeder Eingabeaufforderung Hunderte oder Tausende Bild-Tokens hinzu. Daher erzeugen Kamerapipelines, die Bilder zur Beschreibung senden, genau diese Mischung. Die Ultralytics-LLM-Schnittstelle sendet Text und Bilder an jeden OpenAI-kompatiblen Endpunkt, einschließlich eines selbst gehosteten vLLM-Servers. Dort werden Anfragen einer YOLO26-Pipeline zur Beschreibung der Bilder, auf denen sie Personen erkennt, zusammen mit dem übrigen Datenverkehr in Abschnitten vorverarbeitet.
Das Tokenbudget abstimmen#
Das Budget stellt zwei Latenzkennzahlen gegeneinander ab. Ein kleines Budget lässt in jedem Schritt wenig Raum für Prefill. Dadurch bleibt die Latenz zwischen Tokens für Benutzer mit Streaming niedrig, während sich die Zeit bis zum ersten Token bei neuen Eingabeaufforderungen verlängert. Ein großes Budget bewirkt das Gegenteil. Jeder spätere Abschnitt einer langen Eingabeaufforderung liest außerdem den wachsenden KV-Cache der früheren Abschnitte erneut ein, weshalb sehr kleine Abschnitte den Speicherverkehr erhöhen. Betreiber stimmen das Budget anhand der medianen und der hohen Inferenzlatenz, der Zeit bis zum ersten Token und des Durchsatzes bei realistischen Eingabeaufforderungslängen ab. Der Optimierungsleitfaden für vLLM nennt 2,048 Tokens als Budget, das die Latenz zwischen Tokens begünstigt, und empfiehlt für maximalen Durchsatz mit kleineren Modellen auf großen GPUs mehr als 8,192 Tokens.










