← Insights

EN — English version

#13 · DeepSeek V4 Flash mit TensorSharp: Der Serverpfad steht, die OpenCode-Konfiguration noch nicht

· Filip

Am Ende von Beitrag #11 gaben wir ein bewusst unspektakuläres Versprechen:

Das Modell passt bereits. Jetzt muss die Software lernen, alle acht GPUs gemeinsam zu nutzen.

Und anschließend:

Wir werden berichten.

Hier ist der Bericht.

Die Engine, die damals noch als Kandidat galt, ist inzwischen ein validierter TensorSharp-Serverpfad für unsere Maschine. Wir haben die Kompatibilität mit OpenCode einschließlich Tool-Schleifen geprüft; serverseitig ist der Pfad einsatzbereit. Die derzeit geladene OpenCode-Clientkonfiguration bildet diesen produktiven Zustand allerdings noch nicht ab. Sie deklariert weiterhin 290k Kontext, Temperatur 0 und kein small_model.

Zu diesem Stand sind wir nicht durch plötzlich einsetzende Telepathie zwischen acht RTX 3090 gelangt, und ein magisches --tp 8 hat die Pipeline auch nicht in einen tensorparallelen Supercomputer verwandelt. TensorSharp führt DeepSeek-V4-Flash weiterhin per Layer-Split aus. Ein autoregressiv erzeugtes Token durchläuft vollständige Layergruppen in ihrer Abhängigkeitsreihenfolge. Acht Karten liefern die nötige Kapazität, bündeln aber nicht ihre Speicherbandbreite für jede Matrixmultiplikation.

Geändert hat sich die Software rund um diesen seriellen Pfad. Sie verschwendet weniger Zeit, behält Zustände zuverlässig, teilt Speicher nach gemessenen Bytes statt nach Zuversicht zu und zwingt eine Agentensitzung nicht mehr zum erneuten Prefill von hunderttausend Tokens, nur weil zwischendurch eine andere Konversation an der Reihe war. DeepSeek4 funktioniert nun außerdem als echtes OpenCode-Modell: Werkzeuge kommen als strukturierte tool_calls an, Reasoning und sichtbarer Inhalt bleiben getrennt, und bei einer exakt angehängten Fortsetzung kann die Sitzung ihren KV-Zustand behalten.

Das Ergebnis ist ein realer Endpoint und kein Screenshot eines erfolgreich geladenen Modells. Im aktuellen vergleichbaren Benchmark erreicht er unter dem neuen TG-v1-Protokoll im Median 44,96 tok/s bei kurzem Kontext und 33,75 tok/s bei 100k aktivem Kontext. Der validierte Server hält in einem bewusst engen Betriebsmodus mit 524k Kontext zwei langlebige Konversationen vor. Er kann eine Sitzung mit 500k Tokens zwischenparken, eine kurze unabhängige Sitzung bearbeiten und anschließend zur langen Sitzung zurückkehren, ohne noch einmal 115 Minuten Prefill zu rechnen.

Bevor daraus die aktuelle OpenCode-Produktionskonfiguration wird, müssen wir dort Kontext, Sampling und small_model anpassen und OpenCode neu starten. Die Einschränkungen bestimmen, was der Endpoint tatsächlich leistet.

Was seit Beitrag #11 unverändert gilt

Beitrag #11 handelte vor allem von Kapazität. Das 150,75 GiB große Unsloth-UD-Q8_K_XL-GGUF von DeepSeek-V4-Flash passte auf acht Karten der RTX-3090-Klasse mit jeweils 24 GB. Der Hauptzweig von llama.cpp startete mit einem konfigurierten Kontext von einer Million Tokens, verarbeitete in dieser Konfiguration einen realen Prompt mit 120k Tokens und absolvierte separat einen Lauf mit 500k aktiven Tokens. Für Consumer-Ampere ist das weiterhin ein ungewöhnlich starkes Kapazitätsergebnis.

Ein sauber parallelisierter Decode blieb dabei aus. Die damaligen Wege lassen sich inzwischen knapp zusammenfassen:

Weg Was er belegt hat Weshalb er nicht die Lösung war
Stock-vLLM auf SM86/Q8 Dass der naheliegende TP-Stack hier nicht trägt Kein sauber validierter DeepSeek4-Q8-Pfad für diese Ampere-Konfiguration mit acht GPUs
llama.cpp Mainline Q8-Kapazität, 1M konfigurierter Kontext und ein funktionierender Lauf mit 500k aktiven Tokens Der Layer-Split bleibt über vollständige Layergruppen seriell; Tensor- und Row-Split standen für DeepSeek4 hier nicht zur Verfügung
ik_llama im Graph-Modus Dass DeepSeek4 dort inzwischen erkannt wird Der Lauf fiel dennoch auf Layer-Split zurück; --max-gpu stellte keinen echten Graph-Split her
ds4-longctx mit K=1 MTP Sparse Attention, Fusion und Spekulation können den seriellen Pfad spürbar verbessern Wichtiger historischer Fortschritt, aber nicht der heutige Servermodus und mit dem komprimierten FP16-Cache sehr knapp

Wer die technische Archäologie vollständig nachvollziehen möchte, findet sie weiterhin in Beitrag #11. Für die Verbindung zu diesem Artikel reicht ein Satz: Die Suche auf unserem Rig lieferte keinen validierten echten TP8-Pfad. TensorSharp verbessert den seriellen Weg, den diese Hardware tatsächlich bereitstellt, und macht ihn erheblich sparsamer und betrieblich brauchbarer.

Auch die Hardwarebedingungen blieben gleich. Acht GPUs der RTX-3090-Klasse verteilen sich auf vier PCIe-Root-Complexes. Es gibt kein NVLink als allgemeines Verbindungsnetz, und P2P bleibt deaktiviert. Frühere Versuche lieferten auf dieser Topologie korrupte Nulltransfers. Das ist keine Option, die mit ausreichend Zuversicht doch noch produktionsreif wird.

Die Kommandozeile von TensorSharp lädt zu einem Missverständnis ein. Bei DeepSeek4 wählt --tp 8 acht GPUs für den spezialisierten Executor aus. Dieser legt vollständige, zusammenhängende Layergruppen auf die Karten. Er zerlegt die Matrixarbeit eines einzelnen Layers nicht über alle acht GPUs und führt anschließend auch keine gemeinsame Reduktion aus. Jedes Token durchläuft eine Pipeline. Der Name des Schalters ist generisch, die Ausführungsgeometrie nicht.

Diese Unterscheidung trennt einen belastbaren Befund von einem verdächtig glatten Diagramm.

Drei gewöhnliche Fehler verhinderten den ersten Forward-Pass

Beim frühen TensorSharp-Versuch blockierten drei einzeln banale Bedingungen gemeinsam jeden Start.

Zunächst ließ sich die native GGML-Bridge zwar bauen, gelangte aber nicht zuverlässig in das bereitgestellte Laufzeitpaket. Der korrigierte Ablauf bindet die fertige Bibliothek schreibgeschützt ein, führt den Publish-Schritt in einem temporären Serververzeichnis aus und kopiert exakt diese Bibliothek in die Ausgabe. Ohne diesen Mount läuft der Server nicht mit dem hier beschriebenen nativen Stand. Der Satz „Auf meinem Rechner ließ es sich bauen“ wird mit CUDA im Spiel nur teurer.

Danach scheiterte die Platzierung. Der Solver kannte Bytekapazitäten, aber nicht die besondere Bedingung von DSpark. Das ungefähr 5,99 GB große DSpark-Support-GGUF von TensorSharp ist nicht der alte 3,545-GB-Sidecar von llama.cpp. Es zielt auf die Trunk-Layer 40 bis 42 und muss gemeinsam mit dem Output-Head auf der letzten GPU liegen. Ein früher Split legte Layer 40 auf GPU 6 und den Head auf GPU 7. Inzwischen behandelt der Solver den ersten Ziel-Layer als harte Grenze und platziert ihn, die folgenden relevanten Layer, DSpark und den Head zusammen. Ein ästhetisch ausgewogener Split, der nicht ausführbar ist, bleibt Dekoration.

Der dritte Fehler trat erst nach erfolgreichem Laden auf. Der fusionierte Graph über acht GPUs registrierte 17 Scheduler-Backends: acht CUDA-Backends, acht fusionierte TensorSharp-Backends und einen CPU-Fallback. Das eingebettete ggml war für 16 ausgelegt. Deshalb scheiterte der erste echte Forward-Pass. Ein kompiliertes Maximum von 32 schafft nun Reserve, statt die Existenz des 17. Backends wegzudefinieren.

Anschließend funktionierte der Forward-Pass über acht GPUs mit fusionierten Operationen und Flash Attention. Der Smoke-Test lieferte für 17 * 23 korrekt 391. Auch die vermeintlich ungenutzten 15,5 GiB erwiesen sich als Artefakt einer unterschiedlichen Gerätereihenfolge in CUDA und nvidia-smi. Bei 43 Trunk-Layern gilt ceil(43 / 8) = 6; mindestens drei GPUs müssen daher sechs Layer tragen. In der korrigierten Ansicht nach PCI-Bus-Reihenfolge erfüllte der Split diese Untergrenze bereits.

Ein One-Shot war schnell, eine Unterhaltung zunächst nicht

Nach dem erfolgreichen Start erreichte TensorSharp im frühen CLI- beziehungsweise One-Shot-Pfad 53,3 tok/s bei kurzem Kontext und 40,4 tok/s um 100k. Ein späterer A/B-Test über Streaming-HTTP kam unter seinem eigenen Verfahren mit Temperatur 0, 40 Prompt-Tokens und 256 Ausgabe-Tokens auf 69,33 tok/s bei kurzem Kontext.

Diese Zahlen sind historische Beobachtungen und gehören nicht in die aktuelle Vergleichstabelle. Prompts, Warmups, Sampling und Ausführungspfade unterschieden sich. Dasselbe gilt für ds4-longctx mit 35,98 tok/s kurz und 32,39 tok/s um 100k, llama.cpp Mainline mit 12,20 tok/s bei 500k sowie den späteren extremen Single-Slot-Lauf von TensorSharp mit 18,92 tok/s. Die Einheit tok/s macht zwei Messungen noch nicht vergleichbar. Benchmarks sind da anspruchsvoller.

Gravierender war, dass sich Gespräche damals nicht sinnvoll fortsetzen ließen. Im CLI-Multi-Turn-Pfad konnte eine Fortsetzung bei 100k erneut einen vollständigen Prefill auslösen. Jeder weitere Turn brauchte dadurch ungefähr 342 bis 345 Sekunden. Ein KV-Cache lässt sich kaum gründlicher zweckentfremden.

Die erste Ursache war subtil. Der HTTP-Server übernahm den KV-Zustand des Zielmodells korrekt, verlor beim Besitzwechsel aber den vom Executor verwalteten Kontext von MtpSpeculativeExecution. Ein fortgesetzter Request behielt damit einen Teil seines Präfixzustands auf der Zielseite, fiel jedoch aus der von DSpark unterstützten spekulativen Ausführung. Der Server hatte die Bücher behalten und den Bibliothekar verlegt.

Die Reparatur behandelt diesen spekulativen Kontext als Move-only-Zustand. Er wechselt den Besitzer ausschließlich bei einer sauberen, exakten, allein laufenden Append-only-Fortsetzung, deren Sequenzidentität und physische Position übereinstimmen. Der alte Besitzer gibt dabei veraltete ausstehende Zustände auf; akzeptierte spekulative Enden werden auf die veröffentlichte Position gekürzt. Abbruch, Preemption, Divergenz, inkompatible Position oder ein fehlgeschlagener Rewind führen kontrolliert zu Arbeit nur mit dem Zielmodell oder zu einer sauberen Neuinitialisierung. Spekuliert wird nie gegen den möglicherweise fremden KV-Zustand einer anderen Sitzung.

Das ist mehr als defensiver Programmierstil. Ein Draft, der gegen den falschen Holder verifiziert wird, kann falsche Tokens akzeptieren, ohne zwingend abzustürzen. Ein Crash besitzt wenigstens den Anstand, sichtbar zu sein.

Im reparierten HTTP-Pfad war der Unterschied bei 100k eindeutig. Zwei exakt angehängte Folgeturns verwendeten 99.892 beziehungsweise 99.925 Tokens wieder. Die clientseitige TTFT betrug 0,422 und 0,390 Sekunden statt der Dauer eines vollständigen Prefills. DSpark führte bei beiden Turns reale Verifikation aus. Die Aussage „jede Fortsetzung verarbeitet 100k erneut“ beschreibt damit nur den historischen Fehlerzustand, nicht den aktuellen Server.

DeepSeek4 musste die Sprache von OpenCode lernen

Ein schneller HTTP-Endpoint genügt für einen Coding-Agenten nicht. Das Template transportiert inzwischen Tools, Assistant-Historie, unverändertes JSON und Reasoning. Der Parser erkennt deepseek4, gibt strukturierte tool_calls aus und trennt Reasoning vom sichtbaren Inhalt. Ein erzwungener Werkzeugaufruf lieferte finish_reason: tool_calls samt {"a":17,"b":23}; im Content tauchte kein verirrtes </think> auf.

Diese Parserarbeit führte zum merkwürdigsten Nebenschauplatz des Projekts. Für Implementierung und Review von DeepSeek4 nutzten wir Laguna-Agenten. Der bearbeitete Code enthielt naturgemäß die literalen Zeichenketten <think> und </think>. Eine Reihe stiller Fehlschläge lieferte zunächst den Nachweis eines eng begrenzten Struktur-Token-Defekts in Laguna. Danach fanden wir einen davon unabhängigen Fehler im Reasoning-Basisparser von vLLM: Erschien innerhalb des generierten Inhalts ein Start-Tag, konnte alles davor verschwinden.

Keiner der beiden Defekte gehört zu DeepSeek4 oder TensorSharp. Die DeepSeek-Implementierung lieferte lediglich den Code, der beide sichtbar machte. Tokenanalyse, providerübergreifender Nachweis, Workaround, vLLM-Report und die anschließende Diskussion der Korrektur stehen in Beitrag #12: Die sechs Zeichenketten, die Laguna nicht schreiben kann.

Cache-Wiederverwendung bleibt ein sehr strenger Vertrag. System-Prompt, Tools, serialisierte Historie und Generation-Prompt müssen byte- und tokengenau eine Append-only-Erweiterung bilden. Am Übergang bleiben die roh generierten Tokens erhalten, sodass Tools den Cache nicht beiläufig zerstören. Ein geändertes Schema, anders sortiertes JSON, ein fremder Request oder ein Turn außerhalb dieser Append-only-Regel kann die Wiederverwendung dagegen beenden. Nur deklarierte Tool-Namen werden zu Aufrufen. Weil DSML innerhalb des Datenstroms keine Escape-Möglichkeit besitzt, bleibt eine Kollision mit einem deklarierten Namen als bekannte Grenze bestehen.

Der Cache zählte Graphen wie gleich große Kartoffeln

Nach der korrekten Zustandsübergabe blieben mehrere Fehlerklassen: unnötige Arbeit in einer heißen Schleife, ein Cache ohne Vorstellung von Ressourcenkosten und zwei Lebensdauerfehler, die aus der Entfernung ähnlich aussahen.

Bei einer Temperatur über null kopierte, indizierte, teilsortierte und skalierte der Sampler sämtliche 129.280 Logits. Mit top_k=0 sortierte er sie sogar vollständig. DSpark kann diesen Pfad für mehrere Verifikationspositionen aufrufen. Das Ergebnis waren 63,82 tok/s bei Temperatur 0, aber nur 31,12 tok/s bei 0,1 und 30,81 tok/s bei 1,0. Ein Sweep über top_k 0, 40, 200 und 1000 blieb flach.

Der neue begrenzte Selektor beginnt mit 256 Kandidaten und erweitert die Auswahl nur, wenn Top-p oder Min-p mehr Wahrscheinlichkeitsmasse verlangen. Erst danach wendet er die Temperatur auf die verbleibenden Kandidaten an. Die Sampling-Kette bleibt erhalten, ebenso die wiederhergestellten In-place-Penalties.

Zur Absicherung blieb ein alter Referenzpfad als Oracle erhalten: 160 Referenzen und 74.000 adversariale Fälle. Bei identischen Werten entscheidet nun die aufsteigende Token-ID. Wichtiger war ein numerischer Randfall, den auch eine Reviewer-Mehrheit zunächst falsch bewertete. Eine frühe gepackte Sortierung ordnete positives NaN oberhalb von +Infinity ein und machte einen numerischen Fehler zum garantierten Kandidaten. Die endgültige Ordnung setzt NaN unter -Infinity, also in die sichere Fehlerrichtung. In isolierten historischen Tests stieg der Decode bei Temperatur 1,0 von 30,8 auf 56,2 tok/s. Auch das ist Entwicklungsevidenz und kein TG-v1-Vergleichswert.

Anschließend brachte der Graph-Cache einen Request mit 35.921 Tokens und danach einen mit 14.000 Tokens zum Absturz. Seine Anzahlgrenze behandelte einen großen Prefill-Graph, einen Ein-Token-Decode-Graph, verschiedene DSpark-Verifikationsbreiten und den Graph einer anderen Sitzung als gleich große Kartoffeln. Der Speicherbedarf eines Graphen hängt von seiner Form ab, nicht davon, dass er als ein Eintrag gezählt wird.

Der Cache erfasst nun die tatsächlichen Bytes des Scheduler-Puffers pro Gerät, prüft den freien VRAM der knappsten GPU und entfernt LRU-Einträge bis zu einer Reserve. Geschützte Graphen aktiver Slots bleiben erhalten; nach jeder Entfernung wird das knappste Gerät neu bestimmt. Mit 512 MiB Reserve verliefen fünf von fünf Wechseln korrekt, bei 50,9 tok/s, ohne nutzlose Trims und mit mindestens 519 MiB frei. Eine Reserve von 1.024 MiB löste dagegen acht nutzlose Trims aus und senkte den Durchsatz auf 43,2 tok/s. Der aktuelle 524k-Modus nutzt Graph-Cache 1 und einen Pin. Entscheidend für die Reparatur war jedoch das Byte- und Reservemodell.

Der erste Use-after-free betraf GPU-Arbeit, die noch lief. graph_build_result konnte Scheduler-Puffer freigeben, ohne zuvor seine use_events zu synchronisieren. Eine Eviction gab dann Speicher frei, den asynchrone GPU-Arbeit noch las. Vor dem Freigeben des Schedulers wird inzwischen jedes Event synchronisiert. Das kann blockieren, verhindert aber die vorzeitige Freigabe. Ein späteres Review hielt diesen bereits behobenen Fehler irrtümlich für den Pinning-Defekt; der Destruktorkontext widersprach dieser Diagnose.

Der spätere Fehler war tatsächlich ein hängender Zeiger auf den gerade erzeugten Cache-Eintrag. Bei null Pins, großer Reserve und einem alten LRU-Kandidaten konnte der Trim innerhalb von acquire das eigene frische Ergebnis löschen, bevor dessen Zeiger zurückgegeben wurde. Die Folge lautete double free or corruption. Ein temporärer lokaler Schutz während acquire verhindert das. Pins aktiver Slots bilden davon getrennt eine Aufbewahrungsrichtlinie. Ein Red/Green-Tausch allein anhand des SHA belegte den kausalen Zusammenhang: Die alte Bibliothek endete mit Exit 139, die endgültige antwortete mit HTTP 200 und blieb gesund. Reviewer zu zählen wäre bequemer gewesen. Richtig wäre es nicht gewesen.

Eine Konversation braucht einen Parkplatz

Eine einzelne funktionierende HTTP-Fortsetzung war nur der erste Schritt. Ein OpenCode-Workflow wechselt zwischen Konversationen: lange Hauptsitzung, kleine Nebenaufgabe, Tool-Ergebnisse und sonstiger Verkehr. Nach jedem Wechsel eine lange Unterhaltung neu vorzufüllen wäre technisch korrekt und betrieblich absurd.

Der komprimierte Attention-Pfad von DeepSeek4 besitzt weder einen gemeinsam genutzten, gebatchten Paged-KV-Pool noch einen kopierbaren Snapshot des KV-Zustands. Der generische Retained-Fused-Cache hatte aus einem unbegrenzten Wert für wiederverwendbare Präfixe geschlossen, ein solcher gemeinsamer Pool sei vorhanden. DeepSeek4 hatte diesen Wert geerbt, implementierte aber keine der nötigen Methoden für Retain, Rebind und Discard. Zwei für sich plausible Annahmen ergaben zusammen einen Fehler.

Die Retained-Fused-Korrektur führt eine fähigkeitsbasierte Behandlung für Modelle ein, die pro Sequenz fusioniert arbeiten, aber weder Pool noch Snapshot besitzen. Ein Holder besteht nur aus der nativen Slot-ID. Beim Retain wechselt diese ID in eine Retained-Map, beim Rebind zurück in einen neuen aktiven Request, und ein LRU-Discard gibt den nativen Slot frei. Damit wandert keine 500k-KV-Nutzlast über den Bus, und genau diese Kopie wollten wir uns ersparen.

Der Planner muss eine einzelne sequenzielle Konversation überhaupt erst auf den fusionierten Pfad leiten. Andernfalls wäre Retention ein hübsch getesteter Mechanismus ohne Aufrufer. Das laufende System kann nun exakte Append-only-Konversationen erhalten. Fused MTP wurde auf diesem Retained-Pfad als striktes Opt-in ergänzt. Die Schutzbedingungen binden den Slot unmittelbar vor jedem Schritt, halten den GPU-Compute-Lock, vergleichen neben Sequenzidentität und Position auch die Holder-Identität und verwerfen den spekulativen Zustand bei jeder Abweichung. Eine Position allein genügt nicht. Zwei falsche Holder können dieselbe Position haben, und gerade dann würde ein rein positionsbasierter Test beruhigend aussehen.

Auch die QA brachte hier eine brauchbare Erkenntnis. Nachdem wir drei Schutzbedingungen entfernt hatten, blieb die Testsuite zunächst grün. Das Test-Double erkannte einen falschen Slot anhand unterschiedlicher Positionen und war deshalb blind für einen gefährlichen Tausch bei gleicher Position. Die Tests prüfen nun ausdrücklich die Identität; jede zentrale Schutzbedingung besitzt einen Test, der beim Entfernen rot wird. Eine grüne Suite belegt genau das, was sie prüft. Ob sie dabei unsere eigentliche Sorge trifft, sagt ihre Farbe nicht.

Das aktuelle Retained-Budget muss wörtlich gelesen werden:

TS_RETAINED_FUSED_CACHE_MAX = N
means N completed retained holders + one separate Primary slot.

Budget 2 belegt im Ruhezustand also drei physische Cache-Slots und nicht zwei insgesamt. Für A -> B -> A ist das die Untergrenze, weil A erhalten bleiben muss, während B läuft. Der validierte Servermodus verwendet MAX_CONTEXT=524288, Ubatch 128, Retained-Budget 2, Fused MTP, Prefix-Cache, Graph-Cache 1, einen Graph-Pin und kein P2P. DeepSeek belegt alle acht GPUs exklusiv.

Daneben existiert ein separater Dienst mit Qwen3-1.7B für eine künftige Zuweisung als small_model. Titelgenerierung und andere kleine Nebenrequests sollen dadurch keine wertvolle DeepSeek-Sitzung verdrängen. In der aktuellen OpenCode-Konfiguration ist diese Zuweisung noch nicht geladen.

Auch „warm“ hat eine enge Bedeutung: Der KV-Zustand bleibt für eine exakt angehängte Fortsetzung erhalten. Ein veränderter Prompt, ein fremder Request, der Zustand verdrängt, oder eine nicht aufbewahrte Sitzung kann weiterhin einen kalten Prefill benötigen. Graph- und MTP-Wärme werden zudem nicht gleichmäßig zwischen Holdern geteilt. Diese Erkenntnis war teuer, aber immerhin eindeutig.

Für den Vergleich brauchten wir erst einen neuen Maßstab

Alle abschließenden Durchsatzaussagen verwenden TG-v1, unser aktuelles kanonisches Protokoll mit drei Läufen. Damit brechen wir bewusst mit der früheren Sammlung attraktiver, aber inkompatibler tok/s-Zahlen.

Für jede Kontextgröße startet der Container frisch. Ein durch Nonce abgesicherter kalter Warmup baut den Kontext auf und erzeugt 256 Tokens. TG1 bis TG3 sind echte, thematisch unterschiedliche Append-only-Erweiterungen. Jeder Lauf fordert 256 Tokens mit dem validierten Produktions-Sampling an: Temperatur 1,0, Top-p 0,95, Top-k 0, Min-p 0 und Repetition Penalty 1,0.

Die TTFT weisen wir getrennt aus. Der Decode-Durchsatz berechnet sich so:

(completion tokens - 1) / (time of last output event - time of first output event)

Das erste Token gehört zur TTFT; SSE-Chunks sind keine Tokens. Ausgabe-Tokens durch die Gesamtdauer zu teilen, vermischt Prefill und Decode zu einer Kennzahl, die keines von beidem sauber misst. Während des Benchmarks überwachen wir API, Health, OOM, Neustartzähler und lokalen VRAM. Auch ein schneller automatischer Neustart ist ein Fehler.

Bevor wir die endgültigen Ergebnisse akzeptierten, prüften wir mehrere plausible Erklärungen. Sechs davon waren falsch:

Plausible Erklärung Was sie widerlegte
Der Layer-Split ließ ungefähr 15,5 GiB ungenutzt Solver-Simulation und korrigierte CUDA-/PCI-Aufzählung zeigten, dass der Split die Untergrenze von sechs Layern bereits erfüllte
Ein führender Zeilenumbruch des Assistants zerstörte das Präfix Turns mit denselben Zeilenumbrüchen erreichten vollständige Wiederverwendung
MTP wurde nur selten aktiv Sein Kontext wird einmal pro Konversation aktiviert und bleibt bestehen; wenige Aktivierungslogs bedeuteten keine seltene Spekulation
Sitzungswechsel verursachten 87 Prozent der Kosten Das stimmte für eine bestimmte TTFT-Ansicht, aber nicht für die Wall-Clock-Kosten; dort waren es ungefähr 8 Prozent
top_k=0 verursachte den nicht-gierigen Einbruch Der Sweep über 0, 40, 200 und 1000 blieb flach; verantwortlich war der Temperaturpfad
Zwei Retained-Slots würden passen, weil wir zwei zählten Graphen existierten pro Slot und waren nach Anzahl statt nach Speicherbedarf begrenzt

Ein separater Fidelity-Test über 203.622 Tokens fragte Fakten bei 1, 3, 5, 10, 25, 50, 75, 90 und 99 Prozent der Kontexttiefe ab. Das Modell beantwortete 9 von 9 korrekt. Dieser Befund entschärft Bedenken gegen Prefix-Cache und komprimierte Attention ausschließlich für diese Fakten. Er belegt weder universelle Qualität noch Verständnis bei einer vollen aktiven Million Tokens; beides ist weiterhin ungetestet.

Die aktuellen und direkt vergleichbaren TensorSharp-Ergebnisse lauten:

TG-v1-Metrik Kurzer Kontext 100k aktiver Kontext
Kalter Warmup-Prompt 409 Tokens 100.044 Tokens
Warmup-Prefill 1,99 s / 205,28 tok/s 475,00 s / 210,62 tok/s
TG1 / TG2 / TG3 Decode 44,96 / 44,24 / 45,28 tok/s 33,75 / 37,72 / 33,43 tok/s
Median Decode 44,96 tok/s 33,75 tok/s
Mittelwert / Standardabweichung 44,83 / 0,43 tok/s 34,97 / 1,95 tok/s
Median TTFT 0,58 s 1,13 s
Von MTP akzeptiert 478 / 644 = 74,2 % 504 / 648 = 77,8 %
Niedrigster freier VRAM während TG1-TG3 1.170 MiB 1.038 MiB

Die 100k-Spalte verhindert, dass wir den Decode-Wert für sich feiern. Der kalte Prefill dauerte 475,00 Sekunden bei 210,62 tok/s. Diese Wartezeit fällt immer dann an, wenn kein exakt passendes aufbewahrtes Präfix vorhanden ist. TTFT und Decode stehen getrennt in der Tabelle, weil beide für den Betrieb zählen.

Die kanonischen Rohdaten liegen in short.json und 100k.json. Im selben Verzeichnis befinden sich außerdem die archivierten Dateien current-container-inspect.json, current-models.json, deployed-native-sha256.txt und SHA256SUMS.

Unsere frühere Konfiguration mit Temperatur 0 erzeugte wiederholte Doom Loops. Das war ein Konfigurationsfehler auf unserer Seite und kein Defekt von TensorSharp, Prefix-Cache oder komprimierter Attention. Die gemessene produktive Einstellung lautet 1,0 / 0,95 / Top-k 0 / Min-p 0 / Repeat 1,0.

500.000 Tokens, zwei Sitzungen und keine Speicherreserve

Der betrieblich interessanteste Langkontexttest verwendete die Fused2-Konfiguration: Retained-Budget 2, maximal 524k Kontext, Ubatch 128, Graph-Cache-Limit 1, ein Graph-Pin, Prefix-Cache, Fused MTP, deaktiviertes P2P und acht ansonsten unbeschäftigte GPUs.

Auf einem frisch gestarteten Server führten wir die Folge A, B, A, B, A aus:

  • A begann mit einem kalten Prompt über 500.027 Tokens. Die clientseitige TTFT betrug 6.913,347 Sekunden, also ungefähr 115,2 Minuten.
  • B war ein kurzer, unabhängiger Kontext.
  • Die erste Rückkehr zu A dauerte clientseitig 1,666 Sekunden, die zweite 1,680 Sekunden. Serverseitig lagen die TTFTs bei 990 ms und 967 ms.
  • Bei den beiden Rückkehrschritten verwendete A 500.037 von 500.080 beziehungsweise 500.090 von 500.133 Tokens wieder. kvReused meldete serverseitig 99,991 %.
  • Der warme Rücksprung zu B verwendete 1.358 von 1.400 Tokens wieder und erreichte clientseitig eine TTFT von 0,260 Sekunden.

Der native Holder wurde neu gebunden, die exakten Präfixe wurden wiederverwendet, und alle fünf Antworten waren hinsichtlich Routing und Transport korrekt. cached_tokens und kvReused sind jedoch zwei Ansichten derselben Telemetrie. Außerdem gaben die Fortsetzungen lediglich das erwartete Geheimnis wieder. Der Test ist daher kein unabhängiger Beweis für KV-Integrität.

Für beide Holder blieb nur der KV-Zustand warm. A musste seinen Graph neu aufbauen und verlor ihn später wieder; nur B erhielt den globalen MTP-Kontext. Wir behaupten keine symmetrische Graph- oder MTP-Wärme.

Die Speichergrenze ist ebenso eindeutig. Der native Tiefstwert lag bei 205 MiB, während der Host-Sampler auf der knappsten GPU 211 MiB beobachtete. Budget 2 funktioniert unter exklusiven Bedingungen. Eine Reserve von 205 MiB genügt aber nicht für einen unbeaufsichtigten Dienst. Fremde CUDA-Arbeit auf diesen Karten wäre unter solchen Bedingungen ein sehr kurzes Experiment.

Budget 3 hielt drei kurze Sitzungen warm und bestätigte damit die Semantik des Budgets. Auf der knappsten GPU blieben jedoch nur 105 bis 209 MiB, bevor ein bekannter 500k-Trunk-Graph von ungefähr 333 MiB berücksichtigt war. Das ist eine direkt nachgewiesene Speicherunterdeckung. Einen dritten Retained-Holder mit 500k schließen wir deshalb aus; einen OOM mussten wir dafür nicht absichtlich provozieren.

Davon getrennt existiert ein historischer Extremtest. Mit MAX_CONTEXT=1048576, Graph-Cache 1, einem Pin und deaktiviertem fusioniertem Pro-Sequenz-Modus verarbeitete ein Single-Slot-Lauf 500.008 Prompt-Tokens und erzeugte 128 Completion-Tokens. Der Prefill dauerte 6.691,227 Sekunden bei 74,7259 tok/s, der Decode erreichte 18,9210 tok/s, und der freie VRAM sank auf 9 MiB.

Das war ein Kapazitätstest am Rand, kein aktueller Multi-Session-Produktionsmodus, kein unbeaufsichtigt sicherer Betrieb und kein Nachweis von Verständnis über eine aktive Million Tokens. Die Ausgaben lieferten ein begrenztes Kapazitäts-Oracle. Retrieval über eine Million Tokens, Needle-Finding und Multi-Hop-Reasoning wurden damit nicht geprüft.

Die Frage nach einer vollen aktiven Million bleibt offen. Eine für eine Million Tokens konfigurierte Kontextallokation, ein abgeschlossener Kapazitätslauf mit 500k und ein bestandener Fidelity-Test bei 203k sind drei verschiedene Aussagen. Wir fassen sie nicht zu einer Million besonders gut vermarktbarer Tokens zusammen.

Die Zusammenfassung ohne Marketingzeile

Für die Eignung dieses Endpoints zählt kein heroischer Einzelwert, sondern eine Gruppe klarer Bedingungen:

Aussage oder Betriebsmerkmal Was sie tatsächlich bedeutet Was sie nicht bedeutet
--tp 8 in TensorSharp Acht GPUs tragen die vollständige DeepSeek4-Layerpipeline Echtes TP8; die Karten berechnen nicht gemeinsam jeden Layer
44,96 kurz / 33,75 bei 100k als TG-v1-Median Aktuelle vergleichbare Decode-Werte warmer Fortsetzungen unter validiertem Server-Sampling Latenz eines kalten Prompts oder Rangfolge gegenüber historischen Zahlen
210,62 tok/s beim kalten 100k-Prefill Ein 100k-Prompt lässt sich im getesteten Modus aufbauen Eine kurze Wartezeit; sie betrug 475,00 Sekunden
A/B/A-Wechsel mit 500k und Retention Zwei Holder können bei exakter Append-only-Wiederverwendung ihren KV-Zustand warm halten Symmetrisch warme Graphen und MTP-Zustände oder unabhängiger KV-Integritätsnachweis
Budget 2 Zwei abgeschlossene Retained-Sitzungen plus ein Primary-Slot passen funktional Allgemeiner Mehrbenutzerdienst oder sicherer dritter 500k-Holder
Tiefstwert 205 MiB Der Fused2-Test lief ohne OOM durch Reserve für fremde GPU-Arbeit oder unbeaufsichtigten Betrieb
1M-Single-Slot-Konfiguration Kapazität lässt sich bis zu einem Prompt mit 500.008 Tokens treiben Qualität, Retrieval oder Verständnis bei einer vollen aktiven Million

Sobald der Client passend konfiguriert ist, eignet sich dieser lokale Pfad für wenige disziplinierte und langlebige OpenCode-Konversationen. Beliebiger geteilter Verkehr ist nicht sein Einsatzgebiet. Tools und Prompts müssen exakte Append-only-Fortsetzungen bilden; fremde Requests können Zustände verdrängen. Der Schutz durch ein separates Qwen-small_model ist geplant, aber in OpenCode noch nicht geladen. Alle acht GPUs bleiben exklusiv belegt, P2P bleibt aus, und der nicht committete Arbeitsstand basiert auf e765938. Einen Merge, einen Commit oder eine Übernahme upstream behaupten wir nicht.

Was allgemein nützlich ist und was zu DeepSeek4 gehört

Die Änderungen sind kein monolithischer „DeepSeek-Patch“. Einige betreffen allgemeine Runtime-Technik und sollten getrennt von der modellspezifischen Ausführung bleiben.

Bereich Allgemeine Erkenntnis DeepSeek4-spezifischer Anteil
Nicht-gieriger Sampler Begrenzte Kandidatenauswahl vermeidet vollständiges Sortieren des Vokabulars im heißen Decode-Pfad; NaN muss in die sichere Richtung fallen DSpark-Verifikationspositionen verstärken den gemessenen Effekt
Graph-Cache Anzahlgrenzen sind kein Speichermodell; tatsächliche Bytes je GPU müssen gegen den freien VRAM des knappsten Geräts gerechnet werden Graphformen von DeepSeek4, Pipeline-Parität und Pinning aktiver Slots
Zerstörung des Schedulers Verwendete Backend-Events müssen synchronisiert werden, bevor Scheduler-Puffer verschwinden Der konkrete Lebenszyklus von graph_build_result in diesem Executor
Frischer Cache-Eintrag acquire darf keinen Eintrag zurückgeben, den der eigene Trim bereits zerstört hat Acquire-Pfade und Pins im Graph-Cache von DeepSeek4
Retained Holder Fähigkeitsprüfungen müssen einen fehlenden Pool von einem unbegrenzten gemeinsamen Pool unterscheiden Bewegung nativer DeepSeek4-Slots ohne Snapshot-Möglichkeit
Spekulative Fortsetzung Spekulativer Zustand darf nur bei bestätigter Kontinuität von Identität, Position und Besitz wechseln DSpark, seine Platzierung am Output-Head und slotlokale Ringspeicher
Agentenadapter Rohdarstellungen und tokengenaue Übergänge können Cache-Treue erhalten Jinja-artiges DeepSeek4-Template, DSML-Parser, Reasoning und Tools

Diese Trennung verhindert, dass eine allgemein nützliche Korrektur in einem modellspezifischen Paket verschwindet. Umgekehrt wird besonderes Verhalten von DeepSeek4 nicht als universelle Runtime-Lösung verkauft. Auch bei der Testlage bleiben wir eng an der Evidenz: Das fokussierte C#-Regression-Gate bestand 376 von 376 Tests. In der breiteren Suite blieben sechs bekannte und vom Patch unabhängige Fehler. Fünf entstehen durch eine fehlende native Bibliothek in der Testumgebung, einer ist ein bereits vorhandener Fließkomma-Toleranztest. Diese Befunde machen aus dem lokalen, nicht committeten Arbeitsstand keine Übernahme in den Upstream.

Die Grenzen und Betriebsdaten stehen in der kanonischen Betriebsreferenz, Build-, Lebensdauer-, Test- und Implementierungsdetails im TensorSharp-Entwicklungsprotokoll. Keines dieser Dokumente verspricht, dass die Änderungen bereits irgendwo gemergt wurden.

Die Software hat mehr gelernt als die GPUs

Beitrag #11 endete mit einem Hardwarebefund: Das Modell passte, während die Runtime die acht GPUs noch nicht gut genug gemeinsam nutzte. An diesem Befund hat TensorSharp nichts geändert. Die Ausführung bleibt ein Layer-Split, ein echtes TP8-Ergebnis gibt es weiterhin nicht. Der serielle Abhängigkeitspfad begrenzt den Single-Stream-Decode besonders bei großem aktivem Kontext. Acht GPUs bilden einen sehr großen Speicherraum, aber keine achtfach verteilte Matrix-Engine.

Die Software hat dafür eine Reihe konkreter Probleme beseitigt. Sie startet nun mit der tatsächlichen nativen Bibliothek, behandelt die DSpark-Platzierung als Solver-Bedingung und überträgt bei HTTP-Fortsetzungen alle nötigen Zustände unter strengen Identitätsprüfungen. DeepSeek4 spricht Tools und Reasoning im von OpenCode erwarteten Format, ohne beim erneuten Rendern beiläufig seine Cache-Übergänge zu verlieren. Der Sampler sortiert nicht länger für jede nicht-gierige Auswahl mehrfach alle 129.280 Logits. Der Graph-Cache rechnet mit Speicher statt Einträgen. Zwei ähnlich aussehende Lebensdauerfehler wurden nach ihren tatsächlichen Ursachen getrennt behoben. Eine Retained-Sitzung hat jetzt einen nativen Parkplatz.

Vor allem haben uns die Arbeiten wiederholt gezeigt, dass eine plausible Erklärung, eine grüne Testsuite oder eine Mehrheit von Agenten keine Messung ersetzt.

Damit steht auf dieser Maschine ein serverseitig validierter Pfad: 44,96 tok/s bei kurzem Kontext und 33,75 tok/s bei 100k als aktuelle TG-v1-Mediane, strukturierte Tools, getrenntes Reasoning, validiertes Produktions-Sampling und zwei aufbewahrte langlebige Konversationen innerhalb ausdrücklich enger Grenzen. Zur aktuellen OpenCode-Produktionskonfiguration wird er erst nach der Clientumstellung.

Ein kalter Prefill mit 500k kostet weiterhin ungefähr 115 Minuten. Im 500k-Modus sind nur zwei Retained-Sitzungen tragbar. 205 MiB Mindestreserve reichen nicht für unbeaufsichtigten Betrieb. Der 100k-Prefill läuft zwar mit 210,62 tok/s, dauert insgesamt aber 475 Sekunden. Wiederverwendung verlangt exakte Append-only-Prompts; fremder Verkehr kann Zustände verdrängen. Die Antworten im 500k-Routingtest sind kein unabhängiger Beleg für KV-Integrität, und Verständnis über eine volle aktive Million Tokens bleibt ungeprüft. Sichtbarster offener Punkt ist die geladene OpenCode-Konfiguration mit 290k, Temperatur 0 und ohne small_model. Erst nach ihrer Anpassung und einem Neustart entspricht der Client dem validierten Servermodus.

Diese Einschränkungen entwerten den Endpoint nicht. Sie beschreiben ihn.

Das Modell passte zuerst. Die Software brachte acht Consumer-GPUs nicht dazu, sich wie ein Rechenzentrums-Interconnect zu verhalten. Sie brachte den vorhandenen seriellen Pfad dazu, deutlich weniger Zeit zu verschwenden und endlich eine Unterhaltung im Gedächtnis zu behalten.

Nerd-Anhang: die aktuelle Betriebsform

Das Modell bleibt das 150,75 GiB große, aus fünf Shards bestehende Unsloth-DeepSeek-V4-Flash-UD-Q8_K_XL. Als Draft-Modell verwendet TensorSharp ein eigenes DeepSeek4-DSpark-Support-GGUF: ungefähr 5,99 GB, drei Stufen, Blockgröße fünf und Ziel-Layer 40 bis 42. Der alte K=1-MTP-Sidecar von llama.cpp ist weder dieselbe Datei noch derselbe Mechanismus und darf hier nicht eingesetzt werden.

Die validierte Serverkonfiguration ist bewusst knapp. Sie entspricht noch nicht der aktuell geladenen OpenCode-Clientkonfiguration:

8 visible RTX 3090-class GPUs, exclusive; P2P disabled
MAX_CONTEXT=524288                 TS_DSV4_UBATCH=128
retained fused budget=2            prefix cache=on
fused MTP=on                       graph cache=1, graph pins=1
sampling: temperature=1.0, top_p=0.95, top_k=0, min_p=0, repeat=1.0
future small_model: separate Qwen3-1.7B service, not DeepSeek

Der extreme Single-Slot-Modus ist eine andere Konfiguration: maximal eine Million Tokens Kontext, Graph-Cache 1, ein Pin und deaktivierter fusionierter Pro-Sequenz-Modus. Er dient Kapazitätsuntersuchungen und ist kein Upgrade-Pfad für den produktiven Aufbau mit aufbewahrten Sitzungen.

Für spätere reproduzierbare Vergleiche gilt das dokumentierte TG-v1-Protokoll. Ältere Greedy-Skripte und besonders gelungene One-Shot-Prompts gehören nicht in dieselbe Tabelle. TG-v1 baut einen Nonce-geschützten Kontext kalt auf und misst danach drei verschiedene warme Erweiterungen. TTFT, Prefill, Cache-Wiederverwendung, Decode, MTP-Statistik, Liveness und lokaler VRAM werden getrennt ausgewiesen, soweit sie verfügbar sind.

Eine spätere Runtime, die auf diesem Ampere-System einen korrekten und validierten Graph- oder Tensorparallelpfad für DeepSeek4 beansprucht, muss sich am selben Q8-Ziel, am selben serverseitig validierten Sampling, an getrennten TTFT- und Decode-Werten und an derselben Skepsis gegenüber Langkontextbehauptungen messen lassen.

Wir hatten versprochen zu berichten. Das ist der Bericht.

Kommentar schreiben

Deine E-Mail-Adresse wird nicht veröffentlicht. Der erste Kommentar wird manuell freigegeben.