Vigilant Inference Governor

WP26 — löst die Zerlegung in Quanten die Aushungerung?

Stand: 2026-09-01 · echte GPU, echtes Sprachmodell

Nachtrag 2026-09-08 — das Ergebnis unten ist überholt.

Die Antwort dieses Dokuments lautete „nein". Sie beruhte auf einem Lauf, in dem gar nichts zerlegt wurde: der Auftragszustand entstand erst in der Fortsetzung, also nie (Review-Befund F03). Dass „mit" und „ohne Zerlegung" identische Zahlen lieferten, war der Fingerabdruck dieses Fehlers, nicht ein Messergebnis.

Hinzu kam ein zweiter Fehler: size_quantum rechnete die Dauer eines Quantums rein proportional zur Tokenzahl. Gemessen kostet jeder Auftrag einen festen Sockel von 14–18 ms — bei rund 18 ms Slack ist das der ganze Unterschied. Das Kostenmodell ist jetzt affin (base_cost_us), und die hier eingetragenen 55 Token/s waren um Faktor 4,4 veraltet.

Nach Reparatur beider Punkte, zwei Läufe: die Zerlegung bringt 40–41 statt 1–2 Generierungen (9.300 statt 606 Zeichen) und kostet dafür 7 Punkte Detektor-Abdeckung (98 % → 91 %) und ein verdoppeltes Antwortalter (33 → 65 ms), bei null Protected-Deadline-Misses. Das ist keine Aushungerung mehr, sondern ein einstellbarer Kompromiss.

Zahlen und Rohdaten: ../reviews/2026-09-07/gpu/ERGEBNIS.md. Der Text unten bleibt als Beleg des damaligen Standes stehen.

Die Frage

Gate M3 hat bestätigt, was ADR-0012 vorhergesagt hatte: ein nicht unterbrechbarer Block läuft neben einer 33-ms-Periode nie. ADR-0014 zerlegt generative Aufträge in Quanten, deren Größe sich aus dem Slack bis zur nächsten geschützten Ankunft ergibt.

Die Frage ist nicht, ob der Detektor geschützt wird — das ist belegt. Die Frage ist, ob das Sprachmodell dabei überhaupt vorankommt.

Aufbau

Detektor RF-DETR 512 px, 30 Hz, protected, Shared Memory
Sprachmodell Qwen3-0.6B, vLLM-Backend, best_effort, 64 Token je Auftrag
Kapazität ein Slot — die Slots modellieren die GPU, nicht den Prozess
Messdauer 30 s je Betriebsart

Zwei Triton-Server auf einer GPU. Der Versuch, das onnxruntime-Backend in das vLLM-Image zu transplantieren, scheiterte: die Modelle laden, aber die Ausführung bricht mit CUDNN_FE failure 7: GRAPH_EXECUTION_FAILED ab. Das Backend linkt gegen die cuDNN-Version seines eigenen Images. Vision- und Sprachmodelle brauchen getrennte Server — was auch der Realität entspricht.

Daraus entstand die Konfigurationsoption backend_endpoint je Modell. Die Kapazitätsrechnung bleibt davon unberührt: zwei Server auf einer Karte teilen sich weiterhin eine Ausführungseinheit.

Der Aufrufweg unterscheidet sich — notwendigerweise

direkt zu Triton   -> decoupled, also ModelStreamInfer
über den Governor  -> unär, denn genau das bietet OneTimer an

Das ist keine Unfairness, sondern die Schnittstellenzusage. Generative Backends sind in Triton grundsätzlich decoupled; OneTimer nimmt den Request unär entgegen und übersetzt intern auf den Stream-Aufruf. Ein Client, der auch am Gateway streamen wollte, wird abgelehnt — bewusst, weil gestreamte Requests die Frischelogik umgehen würden (Spec 16.1).

Was ein Quantum kostet

Die entscheidende Messung. ADR-0014 leitet die Quantengröße aus Budget × Token/Sekunde ab — das unterstellt Kosten proportional zur Tokenzahl. Gemessen (Qwen3-0.6B, enforce_eager: true, je 5 Läufe):

Token je Auftrag Dauer
1 23 ms
2 38 ms
4 77 ms
8 147 ms
16 288 ms
32 580 ms
64 1153 ms

Der feste Sockel je Auftrag ist klein — die Gerade schneidet bei rund 5 ms. Teuer ist das einzelne Token: rund 18 ms.

Das ist die eigentliche Schranke. Bei einer geschützten Periode von 33 ms verbraucht schon das feinstmögliche Quantum — ein Token, 23 ms — siebzig Prozent einer Periode. Eine Zerlegung kann keine Lücken schaffen, die kleiner sind als ihr kleinster Baustein.

Ergebnis

30 s je Betriebsart, Maschine bei Last 1,2, CUDA-Graphen aktiv:

Betriebsart Detektor-Abdeckung AoI p95 Generierungen Zeichen
direkt zu Triton 77 % 36 ms 70 21210
Governor ohne Zerlegung 98 % 33 ms 2 606
Governor mit Zerlegung 98 % 33 ms 2 606

Zwei Aussagen, und sie ziehen in verschiedene Richtungen.

Der Schutz wirkt, und zwar deutlich. Ohne Governor fällt die Wahrnehmung auf 77 % ab, während das Sprachmodell die GPU sättigt. Mit Governor 98 %. Das ist das §1.3-Szenario auf echter Hardware, mit einem echten Detektor und einem echten Sprachmodell — und es reproduziert sich über mehrere Läufe.

Die Zerlegung bringt nichts. Identisch mit und ohne, bei 1802 Veto-Ereignissen des Look-ahead. Der Grund lässt sich aus den gemessenen Kosten ausrechnen:

Detektor:  33 ms Periode, ~19 ms konservative Laufzeit
           -> ~14 ms Leerlauf je Periode

Quantum:   13 ms fester Sockel + 4 ms je Token
           -> das kleinstmögliche Quantum kostet 17 ms

Das kleinste Quantum ist teurer als die Lücke, in die es passen müsste. Der Look-ahead vetoiert deshalb korrekt, und keine Wahl der Quantengröße ändert daran etwas.

Der Sockel von 13 ms ist der Preis dafür, dass OneTimer außerhalb des Servers sitzt und nur an Requestgrenzen zerlegen kann. Eine kooperative Ausführung innerhalb von vLLM — dessen eigenes Priority Scheduling — hätte ihn nicht. Sie wäre dann aber kein Drop-in-Governor mehr, und das ist die Produktentscheidung aus Spec 5.1.

Was daraus folgt

WP26 ist umgesetzt und gemessen, aber nicht wirksam in dieser Konfiguration. Damit bleibt ADR-0012 offen: der lange Best-Effort-Job läuft auf einem Slot weiterhin praktisch nicht.

Wirksam wäre die Zerlegung erst, wenn eine der drei Größen sich ändert:

Die ehrliche Konsequenz für die Positionierung: die Zusage „VLM neben Detektor auf einer GPU" gilt bis auf Weiteres ab zwei Ausführungseinheiten, nicht auf einer.

Messrauschen

Ein Lauf auf derselben Codebasis ergab 52 % statt 98 % für den unveränderten Arm „ohne Zerlegung" — ein Schwung von 46 Punkten ohne Codeänderung, bei erhöhter Systemlast. Die Zahlen oben stammen aus Läufen bei Last unter 1,5 und wurden reproduziert.

Wer diese Messung wiederholt, sollte die Systemlast mitschreiben. Eine Latenzmessung neben anderer Arbeit misst die andere Arbeit.

Drei verworfene Messläufe

Dieser Abschnitt steht hier, weil die Zwischenstände plausibel aussahen und falsch waren.

Lauf 1. „Nur mit Zerlegung erzeugt das Sprachmodell etwas." Sah aus wie der gesuchte Beweis, war aber ein Artefakt: beide Vergleichsarme riefen ein decoupled Modell unär auf und scheiterten daran — nicht am Scheduling. Die Zerlegung war an den Aufrufweg gekoppelt; damit hätte die Zeile „ohne Zerlegung" auch bei perfektem Scheduling null gezeigt. Behoben durch eine eigene Konfigurationsangabe decoupled, unabhängig von cooperative.

Lauf 2. Danach erzeugte das Sprachmodell in keinem governten Arm etwas, bei zurueckgestellt 0 und ausgehungert 0. Vermutet wurde die Überlast-Zustandsmaschine. Falsch: die Zähler zeigten angenommen 910, also exakt die Detector-Frames — die Anfragen hatten den Scheduler nie erreicht. Der Generator rief nun in allen Armen den Stream-Endpunkt auf, und das Gateway lehnt den ab.

Was daraus zu lernen ist. Ohne die Ablehnungszähler im Bericht war jede Erklärung eine Vermutung. Sie stehen jetzt drin. Eine Messung, die nur das Ergebnis zeigt und nicht, wo die Arbeit geblieben ist, lädt zum Raten ein.

Reproduzieren

# Vision-Server
docker run -d --name onetimer-triton --device nvidia.com/gpu=all \
  -p 8000:8000 -p 8001:8001 -v $VISION:/models:ro --ipc=host \
  nvcr.io/nvidia/tritonserver:26.06-py3 \
  tritonserver --model-repository=/models --allow-client-shm=true

# Sprachmodell-Server
docker run -d --name onetimer-vllm --device nvidia.com/gpu=all \
  -p 8010:8000 -p 8011:8001 -v $LLM:/models:ro --ipc=host \
  nvcr.io/nvidia/tritonserver:26.06-vllm-python-py3 \
  tritonserver --model-repository=/models

taskset -c 8-15 target/release/wp26