Qwen3.8-27B ist bei uns seit Ende August der Endpunkt, gegen den mehrere Entwickler gleichzeitig mit ihren Coding-Agenten arbeiten. Dahinter stehen zwei Cluster aus je vier RTX 3090 und ein eigener Proxy, der die Anfragen verteilt. Für die Clients hat sich nur die Adresse geändert; Modellname und Schnittstelle sind dieselben geblieben.
In #14 lief dasselbe Modell auf einer einzelnen Karte. Hier geht es um die Ausbaustufe darüber — und um die Frage, die dabei den meisten Aufwand gekostet hat: Was passiert, wenn mehrere Agenten gleichzeitig mit einem 120.000-Token-Prompt ankommen?
Der Aufbau
Der Dienst belegt acht RTX 3090 in zwei Vierergruppen. Jede Gruppe fährt eine eigene Instanz von SGLang, der Inferenz-Engine, die das Modell ausliefert, mit Tensor-Parallelismus 4, kurz TP=4: Jede Gewichtsmatrix ist in vier Teile zerlegt, und nach jedem Schritt führen die vier Karten ihre Zwischenergebnisse zusammen. Die FP8-Gewichte belegen 7,62 GB je Karte.
Dazu kommt spekulatives Dekodieren mit DFlash2: Ein kleines Entwurfsmodell schlägt acht Tokens auf einmal vor, das große Modell prüft sie in einem Durchlauf und übernimmt im Mittel 3,9 davon. Deshalb erreicht eine einzelne Session bei rund 120.000 Tokens Kontext noch 124 bis 133 tok/s.
| Rolle | GPUs | Kontextfenster | Tokenpool | Parallele Anfragen |
|---|---|---|---|---|
| Standard-Cluster | 4 | 262.144 | 800.000 | 6 |
| Langkontext-Cluster (YaRN, optional) | 4 | 524.288 | 650.000 | 6 |
| Proxy | — | — | — | verteilt nach Länge und Auslastung |
Tokenpool und Anfragegrenze sind zwei unabhängige Schranken: Der Pool hält die zwischengespeicherten Gesprächsteile aller Sessions, die Anfragegrenze bestimmt, wie viele davon gleichzeitig rechnen. Unsere Planung rechnet mit fünf Personen zu je zwei bis drei Subagenten, also 10 bis 15 Sessions à rund 100.000 Tokens; auf eine Vierergruppe passt das nicht. Beide Cluster zusammen tragen 1,45 Millionen Tokens und zwölf parallele Anfragen.
Warum zwei Cluster und nicht ein großer
Eine Anfrage durchläuft zwei Phasen: Erst liest der Server den Prompt ein (Prefill), dann entsteht die Antwort Token für Token (Decode). SGLang gibt dem Prefill Vorrang und schaltet den gemischten Betrieb bei spekulativem Dekodieren zwangsweise ab: Ein Decode-Schritt ist hier kein einzelnes Token, sondern Entwurf plus Prüfung über acht Tokens.
Praktisch heißt das: Ein einzelner Prefill über gut 120.000 Tokens hält rund 136 Sekunden lang jede laufende Antwort derselben Instanz an. Bei zwei Instanzen trifft das nur die Hälfte der Nutzer. Das ist der eigentliche Gewinn der Aufteilung, wichtiger als der Durchsatzzuwachs.
Alle acht Karten in eine Instanz zu stecken, wäre einfacher gewesen und war messbar schlechter: Tensor-Parallelismus 8 lag im Kurzlauf, unserem Messprofil mit rund 400 Tokens Prompt, 31 Prozent unter TP=4; beim Decode mit rund 80.000 Tokens Kontext 38 Prozent, bei drei parallelen Sessions 26 Prozent. Die Karten hängen an PCIe ohne NVLink, direkte GPU-zu-GPU-Übertragung ist auf diesem Board defekt und abgeschaltet. Jedes Zusammenführen läuft deshalb über den Hauptspeicher, und mehr Karten je Instanz erzeugen dort vor allem mehr Verkehr.
Der Zusammenbruch, der die Aufteilung erzwungen hat
Am 21. August brach der Dienst unter echter Agentenlast zusammen: Sechs Sessions mit je gut 120.000 Tokens lieferten zusammen 7,78 tok/s, einzelne 0,87 bis 2,54 tok/s, das erste Token kam nach über 190 Sekunden. Ein Speicherproblem war es nicht, der Tokenpool war zu 54 Prozent gefüllt. SGLang reiht Prefill-Aufträge hintereinander und lässt dazwischen kaum Decode zu; sechs kalte Sessions sind 732.000 Tokens Prefill und damit rund zehn Minuten Stillstand für alles andere.
Zwei Einstellungen haben das behoben: --prefill-max-requests 2 begrenzt die gleichzeitigen Prefills, --enable-session-radix-cache schützt den bereits verarbeiteten Teil aktiver Sessions vor der Verdrängung aus dem Cache.
| Sechs Sessions à gut 120.000 Tokens | vorher | nachher |
|---|---|---|
| je Session | 0,87 bis 2,54 tok/s | 34,95 bis 44,02 tok/s |
| gesamt | 7,78 tok/s | 233,07 tok/s |
Faktor 30, gemessen auf der langsameren GPU-Gruppe. Der Zusammenbruch war damit weg, die 136 Sekunden Stillstand je großem Prefill blieben. Dagegen half erst die zweite Instanz.
Der Proxy: warum kein fertiger Router
Die beiden Cluster sind unterschiedlich konfiguriert: Der eine fährt das native Kontextfenster von 262.144 Tokens, der andere 524.288 per YaRN — ein Verfahren, das die Positionskodierung streckt und dem Modell ein größeres Fenster gibt, als es trainiert wurde.
SGLang bringt einen eigenen Router mit: Er schickt eine Anfrage bevorzugt an den Cluster, der ihren Gesprächsanfang schon zwischengespeichert hat, und gleicht sonst die Auslastung aus. Die Prompt-Länge kennt er nicht, das Kontextlimit seiner Instanzen ebenso wenig. Eine zu lange Anfrage beantwortet der Standard-Cluster dann mit HTTP 400 — und erneut zugestellt wird nur bei 408, 429 und 5xx.
Seit dem 4. September steht deshalb ein eigener Proxy davor. Er entscheidet in dieser Reihenfolge:
| # | Bedingung | Ziel | Grund |
|---|---|---|---|
| 1 | über 253.952 Tokens, gezählt als Prompt plus reservierte Ausgabe (262.144 minus 8.192 Sicherheitsabstand) | Langkontext-Cluster | passt nicht mehr in den Standard-Cluster |
| 2 | Session bekannt, dort Platz | bisheriger Cluster | ein Wechsel erzwingt einen kalten Prefill |
| 3 | Standard-Cluster frei | Standard-Cluster | volle Qualität, kein YaRN |
| 4 | Standard voll, Langkontext frei | Langkontext-Cluster | Überlauf statt Leerlauf |
| 5 | beide voll | kürzere Schlange | das Warten übernimmt SGLang |
Regel 4 ist der Grund für den Umbau: Ohne sie konkurrieren zehn kurze Sessions um die sechs Plätze des Standard-Clusters, während der andere leerläuft. Gemessen am 4. September: zehn gleichzeitige kurze Anfragen, sechs auf dem Standard-Cluster, vier per Überlauf auf den anderen, alle in 9,7 Sekunden bedient, keine wartete.
Regel 2 verhindert den teuersten Fehler, denn ein Clusterwechsel bedeutet kalten Prefill: bei rund 80.000 Tokens gemessene 67 Sekunden. Der Proxy erkennt Sessions an einem Hash der ersten 4.000 Zeichen — der Anfang bleibt über alle Runden stabil, während das Gespräch hinten wächst. Zwei Sessions mit identischem Anfang gälten damit als eine; beobachtet haben wir das bisher nicht.
Fällt der Langkontext-Cluster aus oder schalten wir ihn ab, um die vier Karten anderweitig zu nutzen, läuft alles über den Standard-Cluster weiter; ein Wächter prüft das alle fünf Sekunden. Zu lange Anfragen bekommen dann HTTP 503 mit einer Erklärung.
Was das gestreckte Kontextfenster kostet
Qwen dokumentiert die Erweiterung über 262.144 Tokens hinaus selbst und warnt zugleich davor: Alle verbreiteten Frameworks setzen YaRN statisch um, der Streckungsfaktor gilt also auch für einen 20k-Prompt und kann die Qualität kürzerer Texte beeinträchtigen. Umstellen solle man die Positionskodierung nur, wenn lange Kontexte wirklich gebraucht werden.
Wie viel Faktor 2 dabei kostet, ist nirgends gemessen, auch nicht von Qwen. Die einzigen Zahlen stammen aus dem YaRN-Paper zu Llama-2, Faktoren 16 und 32: dort 0,48 bis 1,05 Prozentpunkte bei kurzem Kontext. Dass Faktor 2 weniger kostet, ist eine Vermutung. Die Aufteilung umgeht die Frage, statt sie zu beantworten — kurze Anfragen laufen auf dem unveränderten Modell, nur wirklich lange auf dem gestreckten.
Durchsatz kostet die Streckung jedenfalls keinen.
| Standard, 262.144 | Langkontext, YaRN 524.288 | Differenz | |
|---|---|---|---|
| Decode, Kurzlauf | 120,69 tok/s | 118,88 tok/s | −1,5 % |
| Decode bei rund 80.000 Tokens | 111,10 tok/s | 111,52 tok/s | +0,4 % |
| Zeit bis zum ersten Token, Kurzlauf | 0,31 s | 0,31 s | identisch |
| Prefill bei rund 80.000 Tokens | 1.204 tok/s | 1.216 tok/s | +1 % |
Beide Abweichungen liegen innerhalb der Streuung; allein im Kurzlauf beträgt die Standardabweichung 9,99 tok/s. Dazu passt der Quelltext: Die gestreckten Positionswerte entstehen einmalig beim Start.
Die Kapazität sinkt dagegen spürbar: Eine einzige 512k-Session belegt 524.288 von 650.000 Tokens, also 81 Prozent des Clusters. Alle Clients pauschal auf 512k zu stellen, wäre deshalb ein Fehler — dann drängen sich alle Sessions im Langkontext-Cluster, in den real eine hineinpasst, während der Standard-Cluster leerläuft.
Der größte Einzelgewinn hatte mit Durchsatz nichts zu tun
Bei null laufenden Anfragen zogen die acht Karten dauerhaft 1.030,6 W. Die Ablaufsteuerung von SGLang fragt im Leerlauf durchgehend nach Arbeit: Acht Server-Prozesse drehten mit 90 bis 99 Prozent CPU-Last leer und hielten die Karten im Boost-Takt. Mit --sleep-on-idle wartet die Ablaufsteuerung stattdessen blockierend.
| ohne Flag | mit Flag | |
|---|---|---|
| Leistung, acht GPUs | 1.030,6 W | 255,7 W |
| je Karte | 106 bis 148 W | 22 bis 44 W |
| Leistungszustand | P2 | P8 |
| SM-Takt | 1.695 bis 1.995 MHz | 210 MHz |
| Zeit bis zum ersten Token nach Leerlauf | 67 ms | 99 ms |
| Decode-Durchsatz | unverändert | |
775 W gespart, 75 Prozent, bei einem überwiegend wartenden Rig rund 18 kWh am Tag. Bezahlt wird das mit 32 Millisekunden mehr auf das erste Token, was neben einem sekundenlangen Prefill niemand bemerkt. Unsere Annahme, die Karten blieben wegen des bestehenden CUDA-Kontexts ohnehin in P2, war falsch: Erst die ständigen Zugriffe halten sie oben.
Was unter Teamlast gemessen ist — und was nicht
Die belastbarste Messung stammt vom 24. August: 15 bis 20 Minuten mit drei parallelen Coding-Agenten. Beide Cluster waren damals identisch konfiguriert, den gestreckten Kontext und den eigenen Proxy gab es noch nicht.
| Kennzahl | Instanz 1 | Instanz 2 |
|---|---|---|
| Cache-Wiederverwendung | 89,8 % | 92,2 % |
| Tatsächlich geprefillte Tokens | 153.973 | 149.864 |
| Aus dem Cache bedient | 1.353.924 | 1.770.944 |
| Decode-Durchsatz, Median | 95,4 tok/s | 95,9 tok/s |
| Decode-Spitze | 212,5 tok/s | 178,1 tok/s |
| Warteschlange | durchgehend 0 | durchgehend 0 |
Rund neun von zehn Prompt-Tokens kamen aus dem Cache, weil jede Folgeanfrage nur den neu hinzugekommenen Teil des Gesprächs einlesen muss. Die tatsächliche Prefill-Last lag bei etwa 167 tok/s gegen 1.050 tok/s Kapazität; von Sättigung war der Aufbau weit entfernt.
Was die Zahlen nicht belegen: 20 Minuten sagen nichts über Tage oder über mehr als drei gleichzeitige Agenten, und der Proxy selbst ist unter echter Teamlast noch gar nicht vermessen. Ein Vorfall vom selben Tag ist bis heute ungeklärt: Einer von drei gleichzeitig gestarteten Agenten geriet in eine Endlosschleife, was auf einen beschädigten rekurrenten Zustand deutet und nicht auf die Ablaufsteuerung. Wir haben zwei kurz zuvor eingeführte Einstellungen zurückgenommen; in den 20 Minuten danach trat er nicht wieder auf — ein Indiz, kein Beweis.
Zur Einordnung gehört die Streuung. Die meisten Messreihen stammen vom Vorgänger-Rig mit denselben Karten; nach dem Umbau liegen wir über 54 Läufe rund 11,7 Prozent darunter. Die Streuung beträgt 11,20 tok/s, drei Läufe je Konfiguration ergeben also ±13 tok/s. Effekte unter 15 Prozent sind damit nicht nachweisbar — die YaRN-Messung oben eingeschlossen. Was darunter liegt und was der Proxy im Alltag trägt, zeigt erst längerer Betrieb.