Was ist backend-spezifische Kernel Fusion?
Backend-specific kernel fusion fasst mehrere GPU-Operationen, die normalerweise getrennt laufen würden, in einem spezialisierten Kernel für ein bestimmtes Backend zusammen. Statt ein kleines Programm zu starten, dessen Ergebnis abzulegen, das nächste Programm zu starten und dieses Muster zu wiederholen, führt das Backend einen größeren Teil der Berechnung als Einheit aus.
Das klingt nach einem kleinen Implementierungsdetail, bis ein Modell die Decode-Phase erreicht: also die Phase, in der es Token einzeln erzeugt. Dann kann ein Workload viele kleine, geordnete Operationen enthalten. Die Rechenarbeit jeder einzelnen kann günstig sein, während das Planen der Arbeit und das Verschieben von Zwischenwerten durch den Speicher teuer werden.
Ein GPU kernel ist ein Programm, das parallel auf der GPU läuft. Ein dispatch ist das Starten dieses Programms für eine gewählte Arbeitsmenge. Fusion verringert die Zahl dieser Starts und hält Zwischenwerte, soweit die Operationen das erlauben, in schnellem On-Chip-Speicher, statt sie in den globalen GPU-Speicher zu schreiben und wieder zu lesen.
Einfach gesagt: Fusion macht das Modell nicht klüger und verändert nicht seine mathematische Aufgabe. Sie reduziert den Verwaltungsaufwand und den Speicherverkehr, den die GPU zwischen den Schritten erledigen muss.
Der Teil „backend-spezifisch“ ist entscheidend. Ein portabler Compute Graph kann Matrixoperationen, Reduktionen, Reshapes und elementweise Transformationen beschreiben. Eine effiziente fusionierte Implementierung muss aber Entscheidungen treffen, die von der tatsächlichen Ausführungsumgebung abhängen: von GPU-API, Shader-Compiler, verfügbaren Synchronisationsmechanismen, Subgroup-Layout, Speicherhierarchie und unterstützten Workgroup-Formen. Ein schneller Vulkan-Kernel ist nicht automatisch ein schneller Kernel für CUDA, Metal oder ROCm.
Das Konzept hängt mit Operator Fusion zusammen, also der allgemeineren Idee, Graph-Operationen zusammenzufassen. Hier liegt der Schwerpunkt auf der endgültigen Backend-Implementierung: nicht nur darauf, zwei benachbarte Operationen zu erkennen, sondern auf einem kompilierten oder handgeschriebenen Kernel, der die Eigenschaften einer bestimmten Plattform nutzt.
Warum das jetzt wichtig ist
Das aktuelle Beispiel ist llama.cpps Release b10844 vom 7. September. Sein Vulkan-Backend ergänzte fusionierte DeepSeek-V4-Hyper-Connection-Operationen namens DSV4_HC_COMB, DSV4_HC_PRE und DSV4_HC_POST. Laut Release Notes nahm eine nicht fusionierte Sinkhorn-Combine-Kette auf der genannten gfx1151-Konfiguration rund 32 Prozent der Decode-Operationszeit ein und umfasste etwa 16.000 Dispatches pro Token. Ein fusionierter Combine-Dispatch ersetzt pro Stelle ungefähr 137 strikt geordnete Node-Ausführungen. Release b10844
Diese Zahlen sind kein allgemeingültiger Vulkan-Benchmark. Sie zeigen jedoch etwas Dauerhafteres: Die Inference-Geschwindigkeit kann dadurch begrenzt sein, wie ein Backend einen Graph absenkt und plant, nicht nur durch Parameterzahl, Quantisierung oder die beworbene GPU-Rechenleistung.
Graph lowering bezeichnet die Übersetzung eines abstrakten Compute Graph in Backend-Operationen, Kernel, Speicherlayouts und Synchronisationsschritte. Zwei Runtimes können nominell dasselbe Modell ausführen und dabei deutlich unterschiedlich viele Dispatches, Speicherbewegungen und Synchronisationen verursachen. Deshalb ist „Dieses Modell ist auf meiner GPU langsam“ oft keine vollständige Diagnose.
Die Forschungsliteratur beschreibt Fusion als Weg, die Kosten einer Kette separater GPU-Funktionen zu vermeiden und Zwischenergebnisse im On-Chip-Speicher zu behalten. Sie unterscheidet häufig vertical fusion, die aufeinanderfolgende Operationen verbindet, von horizontal fusion, die unabhängige oder parallele Arbeit verbindet. The Fused Kernel Library
Einfach gesagt: Ein Model Graph ist ein Rezept. Das Backend entscheidet, ob daraus Hunderte winziger GPU-Jobs oder einige größere, für eine bestimmte Maschine entworfene Jobs werden.
Wie ein fusionierter Pfad funktioniert
Betrachte zunächst einen nicht fusionierten Pfad. Eine Runtime sieht eine Operationsfolge mit Abhängigkeiten: Operation B benötigt das Ergebnis von A, C benötigt B; vielleicht liegt dazwischen eine Reduktion oder Normalisierung. Die direkte Backend-Implementierung gibt jeder Operation ihren eigenen Kernel. Jeder Kernel liest möglicherweise Eingaben aus dem globalen Speicher, rechnet kurz, schreibt Ausgaben zurück und übergibt dann an den nächsten Dispatch.
Ein Fusion Pass fragt zuerst, ob eine Region sicher zu einer Einheit werden kann. Die Operationen brauchen kompatible Abhängigkeiten, Formen, Layouts und numerische Anforderungen. Anschließend entsteht eine kombinierte Implementierung. Zwischen-Tensoren, die sonst im globalen Speicher materialisiert würden, können lokale Variablen, Werte im Shared Memory oder register-residente Zustände werden. Register sind der kleinste und schnellste Speicher, der direkt einer ausführenden GPU-Lane zugeordnet ist, aber sie sind begrenzt.
Die Vulkan-Implementierung in llama.cpp ist ein konkretes Beispiel. Ihre Release Notes beschreiben, dass die vollständige Sinkhorn-Combine mit 20 Iterationen in Registern ausgeführt wird und Subgroup-Shuffle-Operationen mit einem festgelegten Lane-Layout für die 4×4-Kombinationsmatrix verwendet werden. Eine subgroup ist eine von der Hardware unterstützte Gruppe von Lanes, die unter einem definierten Ausführungsmodell effizient Daten austauschen kann. Release b10844
Danach muss das Backend weniger sichtbare Zielkonflikte abwägen. Ein größerer Kernel kann Wege durch den globalen Speicher und Dispatch-Overhead reduzieren, aber mehr Register oder Shared Memory verbrauchen. Das kann die occupancy senken: also die Menge gleichzeitiger Arbeit, die die GPU resident halten kann, um Speicherlatenz zu verdecken. Außerdem können ungünstige Synchronisation, schlechte Lane-Auslastung oder hoher Spezialisierungsaufwand für ungewöhnliche Tensorformen entstehen.
Die sinnvolle Analyseeinheit verändert sich deshalb. Frage nicht nur, ob die primitive Operation X schnell ist. Frage, ob die gesamte geplante Region – Start-Overhead, Speicherverkehr, Synchronisation, Registerbedarf und Layout-Konvertierungen – für dieses Backend und diesen Workload besser ist als die nicht fusionierte Alternative.
Was Fusion nicht ist
Fusion ist keine Quantisierung. Quantisierung verändert die numerische Repräsentation von Gewichten oder Aktivierungen und senkt typischerweise Speicherbandbreite oder Rechenkosten. Fusion verändert die Ausführungsgrenze und Planung. Beides lässt sich kombinieren, aber auch ein quantisiertes Modell kann zu viel Zeit in winzigen Dispatches verlieren.
Fusion ist auch kein Batching. Batching erhöht die Menge unabhängiger Arbeit, die gemeinsam verarbeitet wird. Fusion kombiniert abhängige oder eng verbundene Arbeit in einem Kernel. Batching ist oft besonders für Throughput wirksam; Fusion kann selbst bei Batch Size eins wichtig sein, wenn Token-für-Token-Latenz zählt.
Ebenso ist sie mehr als das Aufzeichnen mehrerer Kernel in einem Command Buffer. Command-Buffer-Batching kann CPU-seitigen Submission-Overhead senken, doch getrennte Kernel laufen weiterhin getrennt und können Zwischendaten weiterhin materialisieren. Fusion entfernt oder kombiniert die Kernel-Grenzen selbst.
Schließlich ist Fusion nicht dasselbe wie Autotuning. Autotuning sucht Implementierungsparameter wie Tile-Größen oder Workgroup-Größen. Fusion verändert das Kandidatenprogramm: Sie entscheidet, dass mehrere Operationen eine Implementierung werden sollen. Ein Autotuner kann danach Parameter beider Formen suchen.
Ein praktisches Engineering-Beispiel
Stell dir einen lokalen Coding Assistant vor, der auf einer gemischten Flotte integrierter und Consumer-GPUs interaktiv antworten muss. Nach der Einführung einer Modellarchitektur mit einem neuen Decode-Micrograph entsteht eine Regression. End-to-End-Tokens pro Sekunde sinken, doch Speicherverbrauch und Modellquantisierung sind unverändert.
Die verlockende Reaktion wäre, Quantisierungsformate zu tauschen oder das Backend als ungeeignet abzuschreiben. Eine bessere Untersuchung zerlegt den Ausführungspfad in beobachtbare Teile. Erfasse Traces für repräsentative Prompts und vergleiche Dispatch-Anzahl, Zeit in den längsten Kerneln, Synchronisationspunkte und Speicherverkehr während Prefill und Decode. Enthält Decode Tausende kleine, geordnete Dispatches pro Token, ist das ein Signal, die Graph-Grenzen zu prüfen statt die Modellgewichte.
Angenommen, das Profiling findet eine wiederholte Folge elementweiser Transformationen, Reduktionen und einer Normalisierung mit fester Iterationszahl. Das Team kann prüfen, ob diese Folge im Produktionspfad stabile Formen und Layouts hat. Falls ja, kann ein backend-spezifischer fusionierter Kernel sinnvoll sein. Die Implementierung sollte einen nicht fusionierten fallback bewahren: einen nachweislich korrekten Pfad für nicht unterstützte Geräte, ungewöhnliche Formen und Debugging.
Korrektheit kommt vor dem Geschwindigkeitsdiagramm. Vergleiche fusionierten und Referenzpfad über realistische Batch Sizes, Formgrenzen und numerische Toleranzen. Teste genau die Gerätefamilien, die du betreibst. Das llama.cpp-Release enthält globale und operationsspezifische Schalter zum Deaktivieren der fusionierten Operationen sowie Evaluation Cases über produktive Iterationszahlen, Batch Sizes und Subgroup-/Workgroup-Grenzen. Das ist das richtige Betriebsmuster: Die Optimierung muss unabhängig schaltbar sein, damit sich eine Produktionsregression eingrenzen lässt. Release b10844
Einfach gesagt: Behalte den alten Weg. Ein schneller Kernel, den du nicht abschalten, vergleichen oder isolieren kannst, ist in Produktion schwer vertrauenswürdig.
Wo es scheitert
Fusion hat echte Kosten. Ein Kernel, der auf eine bestimmte Subgroup-Breite, Lane-Zuordnung und Speicheranordnung zugeschnitten ist, braucht auf einer anderen GPU möglicherweise eine andere Implementierung oder einen Fallback. Dynamische Graphs und unregelmäßige Formen können Spezialisierung zu teuer machen. Manche benachbarten Operationen haben inkompatible Layouts; eine Layout-Konvertierung im fusionierten Kernel kann den erwarteten Gewinn aufzehren.
Auch numerisches Verhalten ist ein Thema. Eine andere Arbeitsreihenfolge, veränderte Reduktionsstruktur oder Werte in anderer Präzision können Ergebnisse verändern. „Gleiche Absicht“ genügt bei numerischen Kerneln nicht; definiere Toleranzen und teste die für dein Produkt relevanten Grenzfälle.
Das weniger glamouröse Risiko ist Wartung. Die Literatur zu fusionierten Kerneln nennt manuelle Implementierung, eine begrenzte Menge vorkompilierter Kombinationen und die Komplexität niedriger GPU-Ebenen als praktische Einschränkungen. Modellarchitekturen, Compiler-Verhalten und Geräteunterstützung ändern sich. The Fused Kernel Library Ein spezialisierter Fast Path kann zu einer langfristigen Kompatibilitätsverpflichtung werden.
Und weniger Dispatches bedeuten nicht automatisch mehr Geschwindigkeit. Erhöht Fusion den Registerdruck so stark, dass Occupancy sinkt, oder entsteht eine schlecht austarierte Workgroup, kann ein vermeintlich optimierter Kernel verlieren. Miss auf dem tatsächlichen Zielgerät mit dem tatsächlichen Workload, statt eine niedrigere Dispatch-Anzahl als Beweis zu behandeln.
Was du diese Woche tun kannst
Erstens: Ergänze Backend-Level-Profiling für einen repräsentativen Inference-Workload. Trenne Prefill und Decode und erfasse neben Latenz auch die Dispatch-Anzahl. Damit wird „langsam“ diagnostizierbar.
Zweitens: Suche einen teuren, stabilen Micrograph, statt das ganze Modell fusionieren zu wollen. Gute Kandidaten sind wiederholte Decode-Ketten mit festen Iterationszahlen und erkennbarem Speicherverkehr für Zwischenergebnisse. Prüfe, ob der Nutzen bei deinen realen Batch Sizes und Prompt-Längen bestehen bleibt.
Drittens: Definiere den Kompatibilitätsvertrag vor dem Fast Path: unterstützte Geräte, Subgroup-Annahmen, Formen, Layouts, numerische Toleranz und die Bedingungen für den Fallback. Behandle sie als Produktverhalten, nicht als Kommentare in Shader-Code.
Vergleiche schließlich fusionierte und Referenzpfade fortlaufend in der Evaluation. Führe beide über Formgrenzen und unterstützte Hardware aus. Stelle einen eng begrenzten Kill Switch bereit. Backend-spezifische Kernel Fusion rechtfertigt ihre Komplexität nur, wenn dein Team belegen kann, dass sie dort schneller ist, wo es zählt, und sich sicher zurückziehen kann, wenn sie es nicht ist.