KI & Werkzeuge

128k Kontext zu reservieren kostet nichts. Ihn zu füllen kostet zwei Drittel.

Ich wollte eine Frage beantworten: Was kostet langer Kontext auf einer 24-GB-Karte tatsächlich? Ich habe sie auf zwei Maschinen gemessen und zwei Antworten bekommen, die einander glatt widersprechen. Auf der ersten kostete der Schritt von 8k auf 128k überhaupt nichts. Auf der zweiten kostete derselbe Schritt zwei Drittel davon.

Beide Messungen stimmen. Der Unterschied lag in meiner eigenen Methode, und ihn zu finden war mehr wert als jede der beiden Zahlen. Kontext zu reservieren und ihn zu füllen sind zwei völlig verschiedene Kosten — und fast jeder Benchmark, den ich kenne, meinen eigenen früheren eingeschlossen, misst still das Erste und klingt dabei nach dem Zweiten.

Maschine eins: 128k zu reservieren ist beinahe umsonst

Vier Läufe über zwölf Modelle auf einer RTX 3090 in einer Workstation: 8.192 Token, 32.768, 65.536, dann das jeweilige Modellmaximum. Ollama 0.32.5, Treiber 610.88, KV-Cache q8_0, Flash Attention aktiv, Temperatur 0, drei Seeds je Aufgabe. Mediane neu gerechnet aus dem Protokoll der Einzelanfragen, Generierungen ab 50 Token.

Die Aufgaben sind kurz. Einzeilige Denkaufgaben, ein paar Programmierprompts. num_ctx stand also auf 131.072, und belegt waren davon vielleicht 300 Token.

Sieben der zwölf hielten diese Reservierung vollständig im VRAM und verloren nichts Messbares:

ModellMaximum8kAm MaximumÄnderungVRAM nötig
glm-4.7-flash131.072124,9124,4−0,4 %21,40 GB
ornith:35b131.072118,5122,8+3,7 %21,25 GB
gemma4:26b131.072102,6104,7+2,0 %16,60 GB
deepseek-r1:14b131.07268,168,0−0,2 %21,37 GB
qwen3.6:27b131.07239,239,1−0,3 %20,03 GB
qwen2.5vl:7b128.000120,8124,5+3,1 %9,39 GB
qwen3:8b40.960116,1118,4+2,0 %8,05 GB
Durchsatz in Token pro Sekunde, Kontext reserviert, aber praktisch leer.

Der Grund steht in der VRAM-Spalte. ornith:35b bringt 19,71 GB Gewichte mit und belegt 21,25 GB, wenn 131.072 Token reserviert sind. Ein leerer 128k-KV-Cache kostet rund 1,5 GB. Grouped-Query-Attention mit q8_0-Cache macht die Reservierung nahezu schwerelos — die Faustregel „Kontext frisst VRAM proportional“ ist für diese Modellgeneration falsch.

Das Eine, das doch kostete — und zwar brutal

Die übrigen fünf Modelle brauchten mehr, als die Karte hergibt, also verschob Ollama einen Teil auf die CPU.

Modell8kAm MaximumÄnderungAuf CPUVRAM nötig
laguna-xs-2.1141,0115,2−18,3 %3,7 %19,74 GB
qwen3-coder-abliterated:30b149,479,5−46,7 %10,3 %24,18 GB
qwen3-vl-abliterated:30b141,461,1−56,7 %14,3 %24,59 GB
qwq:32b35,213,0−63,1 %11,2 %24,18 GB
devstral:24b48,413,1−72,9 %13,9 %25,18 GB

Stellt man den CPU-Anteil dem Verlust gegenüber, bewegt sich das Verhältnis kaum: 3,7 % → 18,3 % (Faktor 4,9), 10,3 % → 46,7 % (4,5), 11,2 % → 63,1 % (5,6), 13,9 % → 72,9 % (5,2), 14,3 % → 56,7 % (4,0).

0 %20 %40 %60 %80 %0 %4 %8 %12 %16 %Anteil des Modells auf der CPUDurchsatzverlustlaguna-xs-2.1qwen3-coder-ablqwq:32bdevstral:24bqwen3-vl-ablBand = Faktor 4 bis 5,6
Jedes Prozent des Modells auf der CPU kostet vier bis fünfeinhalb Prozent Durchsatz. Fünf Messungen, ein schmales Band.

Ein einstelliger CPU-Anteil ist kein „bisschen langsamer“. Er halbiert den Durchsatz. Damit ist diese Auslegungsregel binär: Entweder liegt das ganze Modell im VRAM, oder es ist für diese Kontextlänge das falsche Modell. Ollama fährt die entwertete Konfiguration kommentarlos, solange man nicht /api/ps fragt.

Ein Modell fügt sich nicht ins Bild. laguna-xs-2.1 lagert 3,7 % aus, obwohl es insgesamt nur 19,74 GB braucht und damit bequem auf die Karte passt. Eine Erklärung habe ich nicht und werde mir keine ausdenken. Bis das geklärt ist, ist die Schwelle von rund 21,5 GB eine Faustregel und keine Zusage — alles bis 21,40 GB lief sauber, alles ab 24,18 GB lagerte aus, und dazwischen liegt keine Messung.

Der gepaarte Lauf: beide Maschinen, beide Bedingungen

Die erste Fassung dieses Beitrags stützte sich auf zwei Sweeps, die nicht direkt vergleichbar waren. Also habe ich es ordentlich gemacht: dieselbe Messumgebung auf beiden Maschinen, elf Modelle mit byte-identischen Digests, vier Kontextgrößen und auf jeder Stufe beide Bedingungen — Kontext nur reserviert und Kontext zu 84 % gefüllt. Drei Seeds, Generierungslänge fest auf 300 Token. 528 Anfragen, null Fehler — und die Prompt-Token-Zahlen kamen auf beiden Hosts über alle 88 Zellen identisch heraus. Genau das macht den Vergleich gepaart und nicht bloß parallel.

Generierungsgeschwindigkeit in Token pro Sekunde, links reserviert, rechts gefüllt:

Modell8k res.64k res.8k voll64k vollÄnderung gefülltAuf CPU
qwen3-vl-abliterated:30b153,6149,2113,439,1−65,5 %0 %
deepseek-r1:14b69,569,257,925,7−55,6 %0 %
glm-4.7-flash146,1145,0113,258,4−48,4 %0 %
qwen2.5vl:7b136,2133,6111,559,6−46,5 %0 %
laguna-xs-2.1145,6144,9130,678,4−39,9 %0 %
qwen3:8b137,2134,0104,377,5−25,7 %0 %
devstral:24b54,954,751,142,3−17,3 %0 %
qwen3.6:27b41,640,937,931,7−16,4 %0 %
gemma4:26b98,398,383,574,3−11,0 %0 %
ornith:35b107,1108,394,785,6−9,6 %0 %
qwq:32b36,213,431,53,4−89,3 %11,2 %
rulestation. CT 1100 zeigt dasselbe Muster: reserviert flach, gefüllt fallend um 17 % bis 82 %.

Die reservierten Spalten sind eine gerade Linie. Die gefüllten fallen bei jedem einzelnen Modell, auf beiden Maschinen. Und man beachte die letzte Spalte: ausgelagert hat einzig qwq:32b. Die anderen zehn liefen bei jeder Größe vollständig im VRAM und verloren trotzdem bis zu zwei Drittel ihres Tempos. Damit ist der Mechanismus geklärt — das sind die Kosten der Aufmerksamkeit über einen vollen Cache, nicht Speicherdruck.

100 %90 %80 %70 %60 %50 %40 %8k16k32k64kKontextgrößeornith:35b 90%gemma4:26b 89%qwen3.6:27b 84%devstral:24b 83%qwen3:8b 74%laguna-xs-2.1 60%qwen2.5vl:7b 53%glm-4.7-flash 52%deepseek-r1:14b 44%qwen3-vl-abl:30b 35%reserviertgefüllt (84 %)rulestation · je Modell auf den eigenen 8k-Wert normiert
Jedes Modell auf seinen eigenen 8k-Wert normiert. Das obere Bündel ist reservierter Kontext — flach. Das untere sind dieselben Modelle bei 84 % Belegung. qwq:32b fehlt, weil es auslagert und damit einen anderen Effekt misst.

Ein Muster verdient Beachtung: Wer am wenigsten verliert, war schon vorher am langsamsten. devstral und qwen3.6 geben nur 16–17 % ab, starten aber bei 55 und 42 tok/s. Am härtesten zahlen die schnellen Mixture-of-Experts-Modelle — qwen3-vl-abliterated fällt von 153,6 auf 39,1.

Ein Nebenbefund: Dein Bildschirm kostet 1,8 GB

In beiden Maschinen steckt dieselbe GPU. Die eine ist eine Workstation mit angeschlossenem Bildschirm, die andere ein kopfloser Container. Bei 64k lagerte qwq:32b auf der Workstation 11,2 % aus, auf der kopflosen Maschine nur 8,1 % — gleiches Modell, gleicher Kontext, gleiche Ollama-Version.

Die Workstation belegt im Leerlauf rund 1.812 MiB VRAM, weil irgendetwas den Bildschirm ansteuern muss. Auf einer 24-GB-Karte mit einem Modell nahe der Grenze ist genau das die Spanne, die über Auslagern entscheidet. Wer ein großes Modell auf eine einzelne Karte quetscht, gewinnt im Kopflosbetrieb rund 1,8 GB nutzbaren Speicher — und angesichts der vier- bis fünffachen Verstärkung von oben kann das der Unterschied zwischen vollem und halbem Durchsatz sein.

Warum beide Ergebnisse stimmen

Kontext zu reservieren legt einen KV-Cache an. Das sind Speicherkosten, sie fallen einmal an, und dank GQA und q8_0-Quantisierung sind sie klein — die erwähnten 1,5 GB.

Kontext zu füllen legt Token in diesen Cache, und jedes danach erzeugte Token muss über sie alle hinweg aufmerksam werden. Das sind Bandbreitenkosten, und sie fallen bei jedem einzelnen Token an. Ein leerer 128k-Cache sind 1,5 GB unberührter Speicher. Ein voller sind 1,5 GB, die für jedes erzeugte Token gelesen werden müssen.

Die beiden Versuche beantworten also zwei verschiedene Fragen, und nur eine davon ist die, die üblicherweise gemeint ist:

  • „Kann ich num_ctx auf 128k stellen, ohne dafür zu zahlen?“ Weitgehend ja, solange das Modell im VRAM bleibt. Das ist die erste Tabelle.
  • „Was passiert, wenn ich ein 128k-Gespräch tatsächlich führe?“ Du verlierst ein Viertel bis siebzig Prozent Tempo, mit oder ohne Auslagerung. Das ist die zweite.

Und die beiden Effekte summieren sich. Ein Modell, das auslagert und mit vollem Kontext läuft, zahlt beides.

Was das korrigiert

Ein früherer Beitrag von mir berichtete, sieben von acht Modellen hielten 64k im VRAM und verlören dabei 11 % bis 39 %. Auch jener Sweep arbeitete mit kurzen Prompts, seine Verluste können also ebenfalls nicht von der Kontextbelegung kommen — und dieser breitere Sweep auf neuerem Ollama findet unter denselben Bedingungen gar keinen Verlust. Ich traue der Angabe von 11–39 % nicht mehr und kann sie nicht reproduzieren.

Die erste Fassung dieses Beitrags war schlechter. Sie hatte nur die Daten der ersten Maschine und schloss daraus: „Kontext ist beinahe umsonst, nur Auslagern kostet.“ Das liest sich wie eine Antwort auf die zweite Frage, ist aber eine auf die erste. Die zweite Maschine hat es vor der Veröffentlichung aufgedeckt — was genau das Argument dafür ist, dieselbe Sache zweimal auf verschiedener Hardware zu fahren.

Was daraus folgt

  • Frag bei jedem Kontext-Benchmark, ob der Kontext gefüllt war. Sind die Prompts kurz, wurde die Reservierung gemessen und nicht die Nutzung. Das trifft auf die meisten zu, die man findet — und traf auf meinen zu.
  • VRAM großzügig einplanen, Zeit knapp. Der Cache ist billig zu reservieren und teuer zu lesen.
  • Jede CPU-Auslagerung als misslungene Konfiguration behandeln. Bei vier- bis fünffacher Verstärkung gibt es kein hinnehmbares kleines Maß.
  • Das Maximum prüfen. Ollama kappt num_ctx kommentarlos am Modellmaximum — zwei von zwölf Modellen liefen hier weit unter dem, was die Anfrage verlangte.
  • Messen statt rechnen. Allein gemma4 hat ein leeres head_count_kv, getrennte SWA- und globale Schlüssellängen und einen eigenen Bildturm. Jede Formel, die ich versucht habe, verrechnet sich bei mindestens einem Modell.

Offen bleibt: wo die Auslagerungsschwelle zwischen 21,4 und 24,2 GB wirklich liegt, warum laguna schon bei 19,7 auslagert, und der Füllsweep bei 25 % und 100 % Belegung statt nur bei 50 %. Alle drei brauchen Messungen, keine Argumente.