Unter der Haube
Von der Zeichenfläche zu WebGPU: die IR-Leiter
Im Studio zeichnest du einen Workflow — Knoten, Verbindungen, Parameter. Was läuft, ist aber nicht die Zeichnung: Es ist GPU-Code, erzeugt für deine Maschine und ausgeführt direkt im Browser-Tab. Dazwischen liegt eine Leiter von Zwischenrepräsentationen, und beim Abstieg über sie entstehen die Garantien — Machbarkeit geklärt, bevor irgendetwas läuft, Determinismus deklariert statt angenommen, jeder Kernel verdient sich Vertrauen gegen eine Referenz. Die Engine, der diese Leiter gehört, ist der Microkernel aus dem vorigen Artikel; hier sehen wir ihn arbeiten.
Die Leiter hinab, Sprosse für Sprosse#
Die Absenkung verläuft in benannten, inspizierbaren Stufen. Das Workflow-Dokument — der Graph, den du gezeichnet hast — kompiliert zu einem typisierten Programm, dessen Einheit der Block ist (BlockIR): Die Struktur wird explizit — welche Blöcke, in welcher Reihenfolge, unter welchem Kontrollfluss. Numerische Arbeit senkt weiter ab in einen Tensor-Operationsgraphen, die Sprosse der automatischen Differenziation (WebIR), dann in backend-spezifische Kernel — WGSL für WebGPU, daneben die CPU-Referenz (KernelIR) — und zuletzt in einen Ausführungsplan aus konkreten Dispatches, Puffern und einem Speicherplan (ExecPlan). Verteilt sich ein Lauf über Geräte, partitioniert eine weitere Sprosse — der WorkPlan — ihn in verifizierbare Shards.
Jede Sprosse ist ein vollwertiges Artefakt, das du öffnen und lesen kannst. Zwischen Bild und Ergebnis gibt es keine Blackbox; willst du wissen, was die Maschine gleich tut, inspizierst du die Sprosse, die deine Frage beantwortet.
graph (mid-ladder, simplified)
%x = input "dataset" shape [N, D] dtype f32
%w = param "weights" shape [D, K] dtype f32
%mean = reduce_mean %x axis=0 shape [D]
%xc = sub %x, broadcast %mean shape [N, D]
%y = matmul %xc, %w shape [N, K]
plan (computed before any dispatch)
determinism_tier : prod declared, never defaulted
tolerance_class : default resolves to explicit thresholds per tier
peak_memory : parameters + activations + gradients
+ optimizer_state + working
verdict : green fits the declared budgetsSpeicher steht vor dem Lauf fest#
Den Plan gibt es, damit Machbarkeit vor dem ersten Dispatch entschieden ist. Speicher wird als explizite Aufschlüsselung nach Allokationsklassen geplant — Parameter, für den Backward-Pass behaltene Aktivierungen, Gradienten, Optimizer-Zustand und Arbeitspuffer für Kernel-Temporäre — „mysteriöser GPU-Speicher“ hat also keinen Ort zum Verstecken. Mixed Precision fügt ihre f32-Master-Gewichte hinzu. Große Eingaben werden als deklarierte Streaming-Klassen geplant, Chunk-Größen gegen das Budget berechnet statt durch Abstürzen entdeckt, und Tiling behandelt das 128-MB-Binding-Limit von WebGPU automatisch. Diese fünf Klassen — und was zu ändern ist, wenn sie nicht passen — führt Training im Browser im Detail vor.
Die Planung endet in einem von drei Urteilen. Grün läuft im Budget. Gelb läuft mit Warnungen — etwa Streaming, das langsamer ist als ein Lauf im Speicher. Rot wird abgelehnt, und die Ablehnung ist umsetzbar: was scheiterte, warum, und was zu ändern ist — ein kleinerer Batch, Streaming aktivieren, eine andere Präzision. Ein Out-of-Memory mitten im Lauf gilt als Architekturdefekt, nicht als Pech.
Tipp
Öffne den Plan vor einem langen Lauf. Dieselbe Aufschlüsselung, die die Machbarkeit prüft, zeigt, welche Klasse deinen Bedarf dominiert — oft bewegt ihn ein einziger Regler.
Drei Determinismus-Stufen — nie still voreingestellt#
Jeder Lauf deklariert, wie deterministisch er sein muss. Es gibt genau drei Stufen:
debug— bitweise identische Ergebnisse über Läufe hinweg, mit demselben Seed auf demselben Gerät. Auf einer GPU erzwingt das deterministische Kernel: feste Reduktionsreihenfolge, keine atomare Float-Akkumulation — kostet Tempo, kauft Gewissheit.prod— deterministisch innerhalb einer deklarierten numerischen Toleranzklasse; die Alltagshaltung für echte Arbeit.fast— Durchsatz zuerst; Nichtdeterminismus nur erlaubt, wo er als sicher deklariert ist.
Die Stufe wird mit der Arbeit mitgeführt: in Spezifikationen deklariert, zur Ausführung gewählt, mit dem Lauf aufgezeichnet. Sie wird nie still voreingestellt — ein Workflow ohne effektive Stufe scheitert beim Kompilieren mit einer benannten Diagnose, statt stillschweigend eine anzunehmen. Deklarieren zusammengesetzte Teile verschiedene Stufen, gewinnt die strengste — außer du übersteuerst explizit; die Übersteuerung meldet sich mit einer Diagnose, wann immer sie tatsächlich eine strengere Stufe fallen lässt.
Was „dasselbe Ergebnis“ über GPUs hinweg bedeutet#
Klar gesagt: Ergebnisse auf verschiedenen GPUs sind nicht bit-identisch. Gleitkomma-Addition ist nicht assoziativ, und verschiedene Geräte ordnen Arbeit anders. Was über Geräte hinweg gilt, ist Übereinstimmung innerhalb der deklarierten Toleranzklasse — eine von exact, tight, default, loose, jede aufgelöst in explizite Schwellen pro Stufe. Reproduzierbarkeit ist also ehrlich zweistufig: replayable auf dem Orakelpfad (bit-exakt) und convergent auf dem GPU-Pfad (toleranzbegrenzt). Bit-exakte Wiederholung existiert an genau zwei Orten — die debug-Stufe auf demselben Gerät und der CPU-Referenzpfad, der überall deterministisch ist. Ein Plan, der nicht einhalten kann, was du verlangt hast — geräteübergreifende Bit-Exaktheit auf GPUs etwa — wird mit einer Diagnose abgelehnt, nie auf eine schwächere Garantie heruntergestuft, die auf dem Bildschirm gleich aussieht.
Hinweis
Vergleiche Zahlen von zwei Maschinen so, wie es die Plattform tut: innerhalb der deklarierten Toleranz. Zwei GPU-Läufe, die in der Klasse übereinstimmen und in den letzten Dezimalstellen abweichen, sind das erwartete, ehrliche Ergebnis — das ganze Bild steht auf der Honest-Limits-Seite.
Vertrauen wird gegen eine Referenz verdient#
Jede eingebaute GPU-Operation wird gegen eine CPU-Referenzimplementierung validiert — das Orakel — innerhalb ihrer deklarierten Toleranzklasse. Das Orakel ist der Korrektheitsanker, kein Laufzeit-Fallback: Für eine eingebaute Op ist ein fehlender GPU-Kernel ein Bug, nie ein stiller Umweg über langsameren Code. Am Fuß der Leiter ist das geprüfte Artefakt gewöhnliches WGSL — lesbar und prüfbar:
@group(0) @binding(0) var<storage, read> x : array<f32>;
@group(0) @binding(1) var<storage, read> mean : array<f32>;
@group(0) @binding(2) var<storage, read_write> out : array<f32>;
@group(0) @binding(3) var<uniform> dims : vec2<u32>; // rows, cols
@compute @workgroup_size(64)
fn center_columns(@builtin(global_invocation_id) gid: vec3<u32>) {
let i = gid.x;
if (i >= dims.x * dims.y) { return; }
let col = i % dims.y;
// one element per invocation; conformance compares this kernel
// to its CPU reference within the declared tolerance class
out[i] = x[i] - mean[col];
}Nutzer-Compute verdient es genauso#
Nutzergeschriebene Berechnung unterliegt derselben Disziplin, denn sie ist nie selbst-attestiert. Schreibst du einen Kernel, lieferst du auch eine Referenz; die Konformanz erzeugt Fälle aus deklarierter Signatur und Toleranz und beweist die Übereinstimmung. Ein eigener Gradient wird gegen finite Differenzen deines eigenen Forward gradgecheckt. Bestehen prägt ein ConformanceReceipt — profilgebunden, sodass ein Gerät mit anderer GPU-Umgebung die Prüfung lokal erneut ausführt, statt eine Behauptung zu erben, die keine GPU einlösen kann. Das Receipt begrenzt Vertrauen, nicht Ausführung: Auf deiner eigenen Maschine kannst du einen unkonformierten Kernel immer weiterentwickeln. Das ist das ganze Korrektheits-Regime, und beim Dispatch unterscheidet die Engine einen Community-Kernel nicht von einem eingebauten.
Keine GPU? Ein lauter CPU-Pfad, nie eine leere Zeichenfläche#
Nicht jedes Gerät hat WebGPU — WebGPU-Rendering ist zudem ein Capability-Flag, keine garantierte Grundlage, mit Canvas2D und WebGL2 als garantierten Kontexten. Scellis prüft die Fähigkeiten beim Start und gibt jedem Ergebnis einen definierten, sichtbaren Zustand. Keine GPU, aber eine funktionierende CPU-Engine: Ganze Workflows laufen auf dem CPU-Referenz-Backend, hinter einem lauten, dauerhaften Banner — läuft auf der CPU, langsamer, schweres Training eingeschränkt. Keins von beidem: Das Studio öffnet trotzdem zum Ansehen und Erstellen — komponieren, inspizieren, teilen — und sagt genau, warum nichts ausgeführt wird. Was nie passiert, ist eine leere Zeichenfläche oder eine stille Nicht-Antwort.
Das ist der ganze Weg eines Laufs: Eine Zeichnung senkt Sprosse für Sprosse zu WGSL ab; Speicher und Machbarkeit stehen vor dem ersten Dispatch fest; Determinismus ist ein deklarierter Vertrag mit ehrlicher geräteübergreifender Semantik; und jeder Kernel verdient sich sichtbares Vertrauen gegen eine Referenz, die du inspizieren kannst. Dieselben inhaltsadressierten Artefakte, die einen Lauf vertrauenswürdig machen, machen ihn auch per URL reproduzierbar.