Aha!

Warum dieselbe Frage einmal 150 Sekunden dauert und einmal 4

150 000 Tokens Doku, ein Modell auf einem DGX Spark, eine Frage. Ob die Antwort nach 150 Sekunden anfängt oder nach 4, entscheidet allein die Reihenfolge im Prompt. Und diesen Faktor bringt keine Karte, die man kaufen kann.

30. August 2026prefix-cache · ttft · inferenz

Die Messung ist schnell erzählt. 120 echte Doku-Dateien, zusammen 149 597 Tokens, gehen als Kontext an ein Qwen3.8-27B auf einem DGX Spark, dahinter eine Frage. Bis zum ersten Token der Antwort vergehen 149 Sekunden. Dann dieselben Dokumente noch einmal, mit einer neuen Frage dahinter: 4 Sekunden. Und dann diese neue Frage ein drittes Mal, nur diesmal vor den Dokumenten statt dahinter. 150 Sekunden.

Gleicher Korpus, gleiche Frage, gleiche Karte, gleicher Cache. Faktor 36 dazwischen. Das Einzige, was sich geändert hat, ist die Position von zwölf Wörtern im Prompt.

Was in den 149 Sekunden passiert

Bevor ein Modell das erste Token einer Antwort schreiben kann, muss es den gesamten Prompt einmal durch alle Layer schieben. Das ist der Prefill. Für jedes Token entstehen dabei Keys und Values in jedem Layer, und die landen im KV-Cache, damit die Decode-Phase danach nicht bei jedem neuen Token wieder von vorn rechnen muss. Wie groß dieser Cache wird, steht im Stück über den VRAM-Bedarf. Hier geht es um Zeit.

Der Prefill ist bei langen Kontexten die teure Phase, weil er rechengebunden ist und die Spark auf diesem Modell nur rund 1 000 Tokens pro Sekunde schafft, was bei 150 000 Tokens zweieinhalb Minuten ergibt, in denen das Modell nichts ausgibt. Der Nutzer sieht einen Spinner.

Ein Prefix-Cache hebt das Ergebnis dieser Rechnung über den Aufruf hinaus auf, sodass die Keys und Values schon daliegen, wenn der nächste Prompt mit demselben Anfang kommt, und die Engine nur noch rechnen muss, was hinten neu dazugekommen ist. vLLM macht das seit der V1-Engine von selbst. Ohne Marker. Cloud-Anbieter verkaufen denselben Mechanismus als „Prompt Caching“, mit expliziten Markern und einem Rabatt auf den Tokenpreis.

Die vier Szenarien

Alle vier Aufrufe benutzen denselben Korpus. Unterschiedlich ist nur, was davor und dahinter steht.

AufbauZeit bis zum ersten TokenPrefillaus dem Cache
A kaltKorpus + Frage 1149,3 s1 003 tok/s0 %
B warm, identischexakt derselbe Prompt4,12 s36 336 tok/s98,3 %
C warm, neue FrageKorpus + Frage 24,12 s36 311 tok/s98,3 %
D Frage vornFrage 2 + Korpus150,4 s995 tok/s0 %

(Modell qwen3.8-27b in NVFP4 mit FP8-KV-Cache unter vLLM, 262 144er Fenster, Einzelanfragen. Ein zweiter Durchgang deckt sich auf zwei Prozent genau.)

Zwei Dinge daran lohnen einen zweiten Blick.

B und C sind gleich. Eine neue Frage hinter einem bekannten Korpus kostet nichts Messbares mehr als der identische Prompt. Das ist der Praxisfall: Jemand arbeitet mit denselben Dokumenten und stellt die zweite, dritte, zehnte Frage. Jede beginnt nach 4 Sekunden.

Und warum 98,3 % und nicht 100? Von 149 722 Prompt-Tokens kommen 147 200 aus dem Cache. Die restlichen rund 2 500 sind der Rest hinter dem letzten vollständig gecachten Block plus die neue Frage, und die müssen in jedem Fall gerechnet werden. Bei 1 000 Tokens pro Sekunde ist das gut die Hälfte der 4 Sekunden. Der Rest ist Scheduling und das erste Decode-Token.

Warum die Frage vorn alles kaputtmacht

D ist der Fall, der beim ersten Hinsehen keinen Sinn ergibt. Der Korpus lag zu diesem Zeitpunkt vollständig im Cache. 147 200 Tokens, blockweise, fertig gerechnet. Trotzdem 0 % Treffer.

Wie erkennt die Engine überhaupt einen Treffer? Prefix-Caching matcht ab Token 0, blockweise, und die Blöcke hängen über eine Hash-Kette zusammen. Der Hash jedes Blocks wird aus seinem Inhalt und dem Hash aller vorherigen Blöcke gebildet. Sobald ein Block abweicht, sind alle folgenden Hashes andere, auch wenn ihr Inhalt Zeichen für Zeichen identisch ist. Die vorangestellte Frage verschiebt den gesamten Korpus in einen anderen Hash-Raum. Der Cache ist voll. Er passt nur nicht.

Das ist kein Randfall, den man sich konstruieren muss. Ein Zeitstempel im Systemprompt tut dasselbe. „Heute ist der 30.08.2026, 14:03 Uhr“ als erste Zeile, und jeder Aufruf ist wieder kalt. Ein Sitzungsname, eine Nutzer-ID, ein „Du sprichst mit Markus“ ganz oben: kalt. Werkzeugschemata, die aus einem dict oder set kommen und zwischen zwei Aufrufen ihre Reihenfolge wechseln: kalt. Und nichts davon fällt auf, weil das Ergebnis identisch aussieht. Der Dienst wirkt nur langsam.

Warum das keine Hardware ersetzt

Lässt sich das nicht mit einer schnelleren Karte erschlagen? Rechne es einmal durch.

Kalt läuft der Prefill mit 1 000 Tokens pro Sekunde, aus dem Cache mit 36 000. Wer die 4 Sekunden ohne Cache erreichen will, braucht also eine Karte, die beim Prefill 36-mal so schnell ist wie die Spark. Die gibt es nicht. Der Sprung von einer Spark auf eine Rechenzentrumskarte bringt beim Prefill erfahrungsgemäß einen einstelligen Faktor, und der kostet ein Vielfaches. Faktor 36 bringt keine Karte, die du kaufen kannst.

Der Cache dagegen kostet nichts, was nicht schon da wäre. Er lebt im KV-Pool, den die Engine ohnehin reserviert hat, und in vLLM ist er ein Schalter, der standardmäßig an ist. Die 145 Sekunden Unterschied kaufst du also nicht mit Geld. Du kaufst sie mit der Reihenfolge im Prompt.

Umgekehrt heißt das auch: Wer die Reihenfolge falsch hat, kann die Karte aufrüsten, so viel er will, und wird die 4 Sekunden nie sehen. Er optimiert den falschen Faktor.

Was das für einen Agenten heißt

Ein Agent ist für diesen Cache der beste denkbare Fall. Der Verlauf, den er bei jedem Aufruf komplett mitschickt, ist ein wachsender Präfix, denn Turn 10 beginnt exakt so wie Turn 9 und trägt nur ein Werkzeugergebnis und eine Antwort mehr am Ende. Systemprompt, Werkzeugschemata, die ersten Nachrichten. Alles bleibt vorn stehen und bleibt gleich. Solange nichts Variables davorrutscht, zahlt ein Agent den Prefill für jeden Turn nur einmal.

Daraus folgt eine Regel, die sich merken lässt: Statisches nach vorn, Variables ans Ende.

  • Nach vorn gehört, was zwischen zwei Aufrufen gleich bleibt. Systemprompt, Dokumente, Werkzeugschemata, Beispiele.
  • Ans Ende gehört, was sich ändert. Die Frage, der Zeitstempel, der Nutzerkontext, die Sitzungs-ID.
  • Stabil sortieren, was aus einer ungeordneten Struktur kommt. Werkzeuge alphabetisch, Dokumente in fester Reihenfolge.

Woran erkennst du, dass etwas schiefliegt? Am Symptom „immer gleich langsam“. Ein Dienst, der bei der zweiten und dritten Frage auf denselben Dokumenten genauso lange braucht wie bei der ersten, hat vermutlich etwas Variables zu weit vorn. Dem Modell ist das egal. Es rechnet brav. Nur du wartest.

Ein Fall ist erlaubt und sollte dich nicht erschrecken. Wenn ein Agent seinen Verlauf verdichtet, weil das Fenster voll wird, schreibt er den Präfix neu, und der nächste Aufruf ist einmal kalt. Das ist der Preis fürs Verdichten, und er fällt einmal an. Ein Zeitstempel vorn fällt bei jedem Aufruf an.

Was die Messung nicht sagt

Gemessen wurde bei rund 150 000 Tokens, auf einem Modell, auf einer Karte, mit Einzelanfragen ohne Parallellast. Vier Dinge liegen außerhalb davon.

Wie lange ein Präfix im Pool überlebt, bevor ihn andere Anfragen verdrängen, ist offen, denn die Läufe hier lagen Sekunden auseinander, und im Mischbetrieb mit einem Pool von knapp 970 000 Tokens kann ein Präfix deutlich früher verschwinden. Wie sich Treffer unter mehreren gleichzeitigen Clients mit verschiedenen Präfixen verhalten, ebenso. Nicht gemessen. Bei kleinen Kontexten fällt der Effekt zwangsläufig kleiner aus, weil der Prefill-Anteil kleiner ist. Und die Zahlen gelten für dieses Modell; die Mechanik gilt für vLLM allgemein und in der Sache auch für die Cloud-APIs, die ihren Cache über dieselbe Präfix-Logik führen.

Wer nachmisst, stolpert vermutlich über zwei Dinge. usage.prompt_tokens_details liefert bei diesem vLLM-Image immer null, die von OpenAI bekannten cached_tokens gibt es hier also nicht, und die Trefferquote muss deshalb aus den Prometheus-Zählern unter /metrics kommen. Und vllm:prefix_cache_queries_total und vllm:prefix_cache_hits_total zählen Tokens, nicht Blöcke. Die erste Fassung des Messskripts hat mit der Blockgröße 16 multipliziert und 2,3 Millionen „gecachte Tokens“ bei einem 150k-Prompt gemeldet.

Wer das einmal gemessen hat, liest einen Prompt anders. Die Frage ist dann nicht mehr nur, was drinsteht. Die Frage ist, in welcher Reihenfolge.

Passt dazu
Weitere Stücke