Roofline-Diagramm zur Veranschaulichung der Inferenz-Performance
Research

Eric Schreiber

Inferenzgeschwindigkeit von Grund auf verstehen

TL;DR: Der Geschwindigkeitsengpass bei LLM-Inferenz hängt vom Serving-Szenario ab; es können sogar mehrere miteinander interagierende Faktoren den Engpass bilden. Das von Hand durchzurechnen ist mühsam, daher haben wir ein analytisches Modell entwickelt, das uns die Rechenarbeit abnimmt. In diesem Beitrag nutzen wir es, um ein Gefühl dafür zu bekommen, was schnelle Inferenz ausmacht.

Einleitung

Das Training eines Transformers ist ein einmaliger Kostenpunkt. Das Serving hingegen nicht. Jede Anfrage, die ein Modell in der Produktion verarbeitet, braucht einen Forward Pass. In großem Maßstab summieren sich die Kosten für Inferenz erheblich. Trotzdem wird Modell-Inferenz selten zusammen mit der Architektur betrachtet, dabei ist sie überraschend komplex.

Die Inferenz hängt stark von der Modellarchitektur und der Deployment-Strategie ab. Die Wechselwirkung dieser beiden Faktoren macht es schwer zu isolieren, was die Geschwindigkeit in einem bestimmten Setup eigentlich dominiert. Um das systematisch anzugehen, haben wir CHEETAH entwickelt: ein analytisches Modell, das Inferenzgeschwindigkeit von Grund auf simuliert. Es zerlegt jeden Forward Pass in die drei zentralen Engpässe einer Grafikkarte (GPU): Rechenleistung, Datentransfer und Kommunikation. (Im Folgenden nennen wir Szenarien, die von diesen Engpässen bestimmt werden, bei ihren englischen Fachtermina: „compute-bound“, „memory-bound“ und „communication-bound“.) Aus Modellarchitektur, Hardware-Spezifikationen und Parallelisierungsstrategie berechnet es ein oberes Limit für die erreichbare Inferenzgeschwindigkeit.

Abbildung 1 zeigt, dass CHEETAH nach der Kalibrierung den gemessenen Durchsatz über drei Größenordnungen von Batch-Größen gut nachbildet. Noch besser: Es sagt korrekt vorher, wo sich der Engpass ändert. Die vollständige Formulierung und Validierung von CHEETAH findet sich im Anhang.

Validation CHEETAH
Abbildung 1: CHEETAH-Vorhersagen (Linien) und gemessener Durchsatz (Sterne) für DeepSeek v3 bei 2K Sequenzlänge und Batch-Größen, die drei Größenordnungen abdecken. Alle Setups haben die gleichen Ineffizienzfaktoren. Das Modell erfasst korrekt, wo sich der Engpass ändert: generell memory-bound, nur bei sehr hohen Batch-Größen compute-bound.

Im Folgenden benutzen wir CHEETAH, um zu erklären, was die Inferenzgeschwindigkeit begrenzt. Wir schauen uns beide Inferenzphasen getrennt an (Prefill, also die Verarbeitung des Eingabe-Prompts und Decode, bei der wir ein Token nach dem anderen generieren), weil jede von einem anderen Engpass dominiert wird. Wir fangen mit dichten (engl. dense) Modellen an, konkret dem viel analysierten Llama 3.1 70B, gehen dann zu dünnbesetzten (engl. sparse) Architekturen über und schließen mit einem kurzen Blick darauf, wie sich die Prioritäten im Feld über die Zeit verschoben haben.

Was macht schnelle Inferenz aus?

Inferenz lässt sich in zwei Phasen unterteilen: Prefill und Decode. Beide haben je eine direkte Metrik, welche der Nutzer wahrnimmt: Time to First Token (TTFT) und Inter-Token-Latenz (ITL). TTFT ist die Wartezeit bis zum ersten Token; ITL, oder ihr Kehrwert Token pro Sekunde (TPS), misst, mit welcher Rate jeweils neue Token generiert werden.

Generell können wir sagen, dass Prefill compute-bound ist. Das heißt, der FLOP-Durchsatz der GPU („FLOPS“, floating-point operations per second, also wie viele Gleitkommaoperationen (FLOPs) sie pro Sekunde schafft), bestimmt maßgeblich die Prefill-Geschwindigkeit. Decode hingegen ist memory-bound. Hier zählt vor allem, wie viele Daten pro generiertem Token aus dem Speicher der GPU geladen werden müssen und wie viel Speicherbandbreite dafür zur Verfügung steht.

Das Roofline-Diagramm1 in Abbildung 2 verdeutlicht dies. Im Prefill ist die arithmetische Intensität hoch, weil wir große Matrix-Matrix-Multiplikationen haben und dadurch viele FLOPs pro geladenem Byte berechnen. Damit können wir die Rechenleistung der GPU voll ausnutzen und sind compute-bound. Im Decode ist die arithmetische Intensität hingegen extrem niedrig. Das heißt, wir sind tief im Memory-Bound-Bereich. Die Geschwindigkeit hängt folglich davon ab, wie schnell Daten vom Speicher in die Register bewegt werden können. Ein Großteil der verfügbaren Rechenleistung bleibt dabei ungenutzt. Der Hauptgrund dafür ist, dass wir im Decode ein Token nach dem anderen generieren und deshalb Matrix-Vektor-Multiplikationen (oder Matrix-Matrix-Multiplikationen, bei der eine der Matrizen sehr dünn ist) haben. Diese sind weniger effizient, da die einmal geladenen Daten, etwa die Modellgewichte, kaum wiederverwendet werden.2

Roofline Model
Abbildung 2: Roofline-Diagramm für H100 SXM (BF16), farbkodiert nach theoretisch erreichbarer Compute-Auslastung (rot bis grün). Die Effizienz im Decode hängt stark von Batch-Größe (B) und Sequenzlänge (S) ab. Prefill kann theoretisch die Compute-Grenze erreichen, während Decode tief im Memory-Bound-Bereich bleibt. Höhere Batch-Größen verschieben Decode nach rechts und nähern es der Compute-Grenze an, sofern der Speicher groß genug ist für die jeweilige Batch-Größe. Der im Diagramm eingezeichnete Knickpunkt der Roofline („Ridge Point“) beschreibt die arithmetische Intensität der Hardware selbst, also das Verhältnis zwischen Rechenleistung und Speicherbandbreite.

Wie viel Zeit wir in der Prefill- oder Decode-Phase verbringen, hängt stark vom Anwendungsfall ab. Abbildung 3 zeigt, wie sich das Verhältnis mit der Sequenzlänge verändert: In Chat-Szenarien (kurze Eingaben, lange Ausgaben) dominiert Decode, während Workflows mit langen Eingaben (etwa Verwendung von Tools oder „Retrieval-Augmented Generation“ (RAG)) einen erheblichen Teil der Zeit in Prefill verbringen.3

Prefill vs Decode Time Share
Abbildung 3: Anteil der Inferenz-Zeit, der in Prefill oder Decode verbracht wird, für verschiedene Eingabelängen, bei Batch-Größe 1 und 512 Ausgabetoken. Durch die immer wichtigeren agentenbasierten und Tool-Use-Workloads ist Prefill nicht mehr zu vernachlässigen.

Prefill

Die Prefill-Phase verarbeitet den gesamten Eingabe-Prompt in einem einzigen Forward Pass.4 Dabei wird jede Matrix-Operation des Forward Passes auf einmal auf die gesamte Sequenz angewendet; jeweils eine große Matrix-Matrix-Multiplikation. Dadurch wird jede Gewichtsmatrix nur einmal geladen und über alle Token des Prompts wiederverwendet. Dies bedingt eine hohe arithmetische Intensität. Der Rechenaufwand skaliert für die linearen Projektionen (Q, K, V, O, MLP) mit O(B×S)\mathcal{O}(B \times S) und für die Attention (Q×KTQ \times K^T und Scores×V\text{Scores} \times V) mit O(B×S2)\mathcal{O}(B \times S^2). Bei kurzen Sequenzen dominieren die linearen Projektionen (also Matrix-Multiplikationen), doch bei langen Sequenzen überwiegt der quadratische Attention-Anteil deutlich (Abbildung 4).

Prefill Compute Scaling
Abbildung 4: Zerlegung der Prefill-Zeit. Unter etwa 4K Eingabetoken dominieren die linearen Projektionen. Danach überwiegt zunehmend die quadratische Attention-Berechnung die Laufzeit. Die gestrichelte Linie zeigt, dass die Ladezeit der Gewichte aus dem GPU-Speicher (hier: High Bandwidth Memory, HBM) im Vergleich zu der Berechnungszeit vernachlässigbar ist. Prefill ist klar compute-bound.

Was die Prefill-Geschwindigkeit bestimmt:

  • Die Anzahl der FLOPs des Modells: Da wir compute-bound sind, bestimmt die Zahl der FLOPs direkt die TTFT. Weniger Attention Heads, kleinere Hidden Dimensions oder dünnbesetzte Attention-Muster senken die TTFT.
  • Die Sequenzlänge: Attention skaliert quadratisch. Genau deshalb werden dünnbesetzte und lineare Attention-Mechanismen aktiv erforscht (z. B. linear attention, Mamba, GDN, DSA, MSA und weitere).
  • Die Rechenleistung der GPU: Anders als beim Decode profitiert Prefill direkt von mehr Rechenleistung. Einerseits hilft schlicht schnellere Hardware. Ist man an eine bestimmte Hardware gebunden, können andererseits zum Beispiel Datenformate mit reduzierter Präzision helfen.

Prefill ist vergleichsweise einfach effizient zu gestalten: Große Matrixmultiplikationen nutzen die Tensor Cores5 der GPU gut aus. Die eigentliche Herausforderung ist, die TTFT bei langen Kontexten niedrig zu halten, wo der quadratische Attention-Aufwand dominiert. Kein Nutzer möchte erst einmal eine Minute warten, bevor die Antwort zu erscheinen beginnt. Das motiviert die Suche nach Attention-Mechanismen mit sub-quadratischer Laufzeitskalierung.

Vorsicht mit durchschnittlichen TTFT-Zahlen

Eine interessante Beobachtung ist, dass die global gemessene TTFT davon abhängt, wie die Inference-Engine6 ihre Prefill-Anfragen plant. Nehmen wir BB identische Prefill-Anfragen, jede lang genug, dass fixe Overheads vernachlässigbar sind. Das Prefilling einer einzelnen Anfrage dauert tprefillt_{prefill}. Da wir compute-bound sind, skaliert die Zeit linear mit BB, da jede Anfrage die gleiche Anzahl FLOPs berechnet.

Schauen wir uns zwei Scheduling-Strategien an: (1) den gesamten Batch gleichzeitig prefillen, sodass alle die gleiche TTFT bekommen, oder (2) die Anfragen nacheinander abarbeiten, was einigen eine bessere TTFT gibt als anderen. Für die durchschnittliche TTFT gilt für B>1B>1:

TTFTStrategie 1=BB×B×tprefill=B×tprefill,TTFTStrategie 2=1B×i=1Bi×tprefill=1B×B(B+1)2×tprefill=B+12×tprefill.\begin{aligned} \overline{TTFT}_\text{Strategie 1} &= \frac{B}{B} \times B \times t_\mathit{prefill} = B \times t_\mathit{prefill}, \\ \overline{TTFT}_\text{Strategie 2} &= \frac{1}{B} \times \sum_{i=1}^B i\times t_\mathit{prefill} = \frac{1}{B}\times \frac{B(B+1)}{2} \times t_\mathit{prefill} = \frac{B+1}{2} \times t_\mathit{prefill}. \end{aligned}

Eindeutig TTFTStrategie 1>TTFTStrategie 2\overline{TTFT}_\text{Strategie 1} > \overline{TTFT}_\text{Strategie 2} für jede Batch-Größe. Strategie 2 gewinnt durch eine ungleiche Verteilung. Die erste Anfrage kommt sofort dran, die letzte wartet die volle Zeit B×tprefillB \times t_{prefill}. Batching dagegen gleicht alle TTFTs einander an; jedoch auf den schlechtesten Wert.

In der Praxis ist dieser Effekt schwächer, weil Anfragen zu unterschiedlichen Zeiten ankommen. Die lineare Skalierung ist außerdem nur eine Näherung. Dadurch ist die Lücke zwischen den beiden Strategien kleiner, als die Formeln es vermuten lassen. Qualitativ bleibt das Argument dennoch gültig. Ein Inferenz-Anbieter sollte deshalb die gesamte TTFT-Verteilung im Blick haben, nicht nur den Mittelwert.

Decode

Decode erzeugt Token Schritt für Schritt. Jeder Schritt lädt die vollständigen Modellgewichte aus dem Speicher, braucht aber wenig Rechenleistung pro geladenem Byte. Die arithmetische Intensität liegt daher unter dem Knickpunkt der Roofline. Das macht Decode in den meisten Szenarien fundamental memory-bound. Die Zeit pro Decode-Schritt beträgt näherungsweise:

tdecodeGewichte+KV-CacheSpeicherbandbreitet_{\mathit{decode}} \approx \frac{\text{Gewichte} + \text{KV-Cache}}{\text{Speicherbandbreite}}

Abbildung 5 visualisiert das für Llama 3.1 70B. Die MLP-Gewichte und Attention-Projektionsgewichte bilden eine fixe Untergrenze für die Kosten, die bei jedem Decode-Schritt anfällt. Diese Kosten können aber über die Batch-Größe amortisiert werden. Der KV-Cache kommt als variabler Anteil dazu, der linear mit der Zahl der gecachten Token wächst (Batch-Größe × Sequenzlänge).

Decode Memory Loading
Abbildung 5: Das blaue Band sind fixe Kosten pro Decode-Schritt. Der KV-Cache (rot) wächst linear mit den kombinierten gecachten Token. Bei etwa 400K kombinierten gecachten Token, was grob 50 Sequenzen mit je 8K Token sind, entspricht die KV-Cache-Ladezeit bereits der fixen Gewicht-Ladezeit. Das Design dieser Abbildung ist inspiriert durch Beiträge von Tensor Economics.

Die zentrale Erkenntnis für eine effiziente Decode-Inferenz ist Batch-Amortisierung: Das Laden der Gewichte ist der dominante Fixkostenblock und der fällt nur einmal pro Batch an, egal wie viele Anfragen darin stecken. Abbildung 6 zeigt dies visuell. Die Kehrseite ist eine erhöhte Latenz für jede einzelne Anfrage. Der KV-Cache jeder einzelnen Anfrage wird bei jedem Schritt separat geladen und kann nicht geteilt werden. Mit wachsender Batch-Größe steigt dadurch die ITL für jeden einzelnen Nutzer. Batching tauscht also Per-Anfrage-Latenz gegen Durchsatz. Ein Modell-Anbieter wird folglich versuchen den Durchsatz zu maximieren unter der Einschränkung, jeder Anfrage eine minimale TPS zu garantieren.

Batch Amortisation
Abbildung 6: Links: Per-Anfrage-Kostenaufschlüsselung, die zeigt, wie das Laden der Gewichte (blau) über die Anfragen in einem Batch amortisiert wird, während die KV-Cache-Kosten (rot) pro Anfrage anfallen. Rechts: Der Gesamtdurchsatz skaliert nahezu linear mit der Batch-Größe (beachte die logarithmische x-Achse), wobei KV-Cache-Laden einen immer größeren Anteil der Gesamtzeit ausmacht. Beide Plots verwenden eine konstante Sequenzlänge von 4096 Token.

Decode ist aber nicht in allen Betriebspunkten gleich stark memory-bound. Der Engpasstyp verschiebt sich je nach Batch-Größe und Sequenzlänge. Es ergeben sich vier qualitativ unterschiedliche Regime (Abbildung 7):

Four Decode Regimes
Abbildung 7: Die vier Decode-Regime. Jeder Quadrant zeigt einen anderen Betriebspunkt mit eigenen Eigenschaften und Optimierungsansätzen.

Kleine Batch-Größe, kurze Sequenzlänge

  • Deutlich memory-bound; die Modellgröße (Parameterzahl × Bytes pro Parameter) bestimmt direkt die TPS
  • KV-Cache ist im Vergleich zu den Gewichten vernachlässigbar
  • TPS pro Anfrage ist nahe am Maximum
  • Optimierungshebel: Quantisierung auf niedrigere Präzisionen (FP8 oder INT4), um die Gewichte kleiner zu machen

Große Batch-Größe, kurze Sequenzlänge

  • Gewichte werden über viele Anfragen amortisiert; dadurch hat das System ein höherer Gesamtdurchsatz, aber eine niedrigere TPS pro Anfrage
  • Bei sehr großen Batch-Größen kann man sich dem Compute-Bound-Regime nähern
  • Zeigt das eigentliche Ziel von Inferenz: Durchsatz maximieren bei garantierter TPS pro Anfrage
  • Speichergröße der GPU und garantierte TPS pro Anfrage limitieren die Batch-Größe
  • Optimierungshebel: Dieses Regime ist am effizientesten. Für mehr Geschwindigkeit skaliert man horizontal mit mehr GPUs und größerer Parallelisierung

Kleine Batch-Größe, lange Sequenzlänge

  • Das Laden des KV-Caches dominiert die Zeit pro Schritt (kann bei langen Kontexten die Ladezeit für Gewichte übersteigen)
  • Memory-bound, aber in diesem Fall durch den KV-Cache und nicht durch die Gewichte
  • TPS pro Anfrage verschlechtert sich linear mit der Sequenzlänge
  • Optimierungshebel: Komprimierung des KV-Caches (GQA/MLA/HCA), quantisierter KV-Cache, dünnbesetzte Attention-Muster, die Teile des Caches überspringen (z. B. DSA oder ähnliche)

Große Batch-Größe, lange Sequenzlänge

  • Sowohl KV-Cache als auch Attention-Compute sind groß
  • GPU-Speicher ist die harte Grenze: Der KV-Cache vieler langer Sequenzen erschöpft den Speicher und zwingt dazu, laufende Anfragen zu verdrängen (Preemption)
  • Optimierungshebel: Multi-Node-Serving (mehr Speicher), KV-Cache-Offloading, Prefill-Decode-Disaggregation7, dedizierte Server, die nur Long-Context-Anfragen bearbeiten

Abschließend zeigt Abbildung 8 die arithmetische Intensität über alle Betriebspunkte und macht damit sichtbar, wie nahe verschiedene Szenarien dem optimalen Compute-Bound-Regime kommen.

Decode Bottleneck Heatmap
Abbildung 8: Arithmetische Intensität über Batch-Größen und Sequenzlängen. Fast der gesamte Betriebsraum ist memory-bound (blau). Nur bei sehr großen Batch-Größen mit kurzen Sequenzen nähert sich Decode dem Compute-Bound-Bereich (rot); allerdings wäre die TPS pro Anfrage dann zu niedrig für normalen Chat-Betrieb. Diese Szenarien sind nur für die Erzeugung großer Datenmengen sinnvoll.

Was ändert sich bei MoE-Modellen?

Die bisherige Analyse geht von dichten Modellen aus, weil sie einfacher zu analysieren sind. Mixture-of-Experts (MoE)-Architekturen haben sich aber klar als Standard etabliert und verändern das Bild in einigen wichtigen Punkten. Anders als bei einem dichten Modell, bei dem jeder Parameter für jeden Token genutzt wird, aktiviert ein MoE-Modell pro Token und Modellschicht nur eine kleine Teilmenge seiner Parameter, die sogenannten Experten. Die FLOPs pro Token sind dadurch deutlich niedriger, als die Gesamtparameterzahl vermuten lässt. Diese Dünnbesetztheit (engl. Sparsity) ermöglicht sehr große Modelle, was wiederum große Multi-GPU-Setups erfordert, allein um die Gewichte im Speicher zu halten.

Dieselbe Sparsity verändert auch, in welches Engpass-Regime man gerät. Jedes einzelne Experten-MLP ist klein. Das Aufteilen der Gewichtsmatrizen bei Tensor-Parallelität (TP) würde diese so schmal machen, dass die Tensor Cores der GPU nicht mehr vollständig genutzt würden. Die naheliegende Lösung ist Experten-Parallelität (EP): Verschiedene Experten werden statisch verschiedenen GPUs zugewiesen. Da jeder Experte nur den Bruchteil des Batches sieht, der ihm zugewiesen wird (Abbildung 9), bleibt die Batch-Größe pro Experte länger klein. Man braucht also einen deutlich größeren Batch als bei dichten Modellen, um in die Nähe des effizienten Compute-Bound-Regimes zu kommen.

EP erfordert zudem das Kommunizieren der Token zur GPU des jeweiligen Experten, was bei jeder Schicht mehrere All-to-All-Kommunikationsschritte einführt. Bei höheren Batch-Größen werden nahezu alle Experten aktiviert, sodass jede GPU Token mit jeder anderen austauschen muss. Diese Kommunikation kann zum dominanten Engpass werden und Decode bei MoE-Modellen sogar communication-bound machen, was potenziell noch langsamer als memory-bound ist.

Expected Unique Routed Experts
Abbildung 9: Erwartete Zahl aktivierter Experten für eine Schicht mit 8 aktiven Experten von 256 insgesamt. Bereits bei moderaten Batch-Größen (~94) werden 95 % aller Experten aktiviert, nahezu alle Gewichte müssen also geladen werden. Diese Abbildung ist rein theoretisch; in der Praxis sind manche Experten deutlich häufiger aktiv als andere, was die Verteilung nach rechts verschiebt. Das statistische Modell wurde erstmals in unserem Theoretischen Inferenz-Modell für DeepSeek beschrieben.

Prefill oder Decode: Wofür optimieren?

Historisch lag der Fokus vor allem auf Trainingseffizienz. Modellarchitekturen wurden typischerweise auf FLOP-neutraler Basis verglichen, was eine guter Indikator für die Leistung eines Models ist, aber nicht für Inferenzgeschwindigkeit ist. In jüngerer Zeit hat sich das Feld in Richtung inferenzeffizienter Architekturen verschoben, mit einem Schwerpunkt auf Decode-Effizienz. Der Grund ist ökonomisch: Ein Modell wird einmal trainiert, dann aber über seine gesamte Lebensdauer viel benutzt. Die einmaligen Trainingskosten werden irgendwann von den kumulierten Inferenzkosten überholt.

Gerade verändert sich dieser Schwerpunkt erneut. Mit Architekturen wie DeepSeek v4 sieht es danach aus, dass Frontier-Labs zunehmend für beide Phasen optimieren, nicht mehr nur für Decode. Warum das wiedererwachte Interesse an Prefill? Tool-Use- und RAG-Workflows werden immer verbreiteter. Diese Systeme bringen oft große Mengen Kontext mit, der vor der Generierung verarbeitet werden muss. Damit verschiebt sich das Verhältnis von Prefill zu Decode gegenüber einem normalen Chat. Der breite Einsatz von Speculative Decoding8 hat zum gleichen Effekt beigetragen.

Was diese Entwicklung zeigt: Das richtige Optimierungsziel ist nicht unveränderlich. Mit sich wandelnden Serving-Mustern verschiebt sich auch der Engpass. Genau deshalb lohnt sich das Grundverständnis: Wer versteht, was eine GPU gerade tut, und wo der aktuelle Engpass liegt, kann abschätzen, wo die nächste Grenze auftauchen wird.

Anhang: CHEETAH

CHEETAH — Compute Heuristics for Estimating Execution Time And throughput on given Hardware.

Inferenzgeschwindigkeit hängt von vielen miteinander interagierenden Faktoren ab. Es ist schwer zu überblicken, was eine einzige Änderung alles beeinflusst. Den Attention-Mechanismus anpassen, die Parallelisierungsstrategie optimieren, zu prüfen ob der Workload compute-, memory- oder communication-bound ist; das alles von Hand zu tun ist aufwändig und fehleranfällig. CHEETAH ist ein Versuch, diese Engpässe von Grund auf zu simulieren und schnell zu iterieren. Es reduziert Architektur, Parallelisierungsstrategie und Hardware auf eine Handvoll Parameter, auf deren Basis wir die bestmögliche Inferenzgeschwindigkeit berechnen können, ohne bereits eine vollständige Implementierung zu benötigen.

Die vorhergesagten absoluten Zahlen stellen eine theoretische Geschwindigkeitsobergrenze dar und sind in der Praxis nur annähernd erreichbar. Was CHEETAH richtig erfasst, ist die Struktur: Wo sind die Engpässe, und wie schneiden Architekturen und Setups im Vergleich ab? Bei guten Implementierungen ist dieses Rangfolge stabil und genau das macht das Modell für die Erkundung des Design-Raumes nützlich.

Dieser Anhang beschreibt das Design, die Formulierung, Einschränkungen und die Validierung von CHEETAH.

Was CHEETAH modelliert

CHEETAH schätzt die Zeit für einen einzelnen Forward Pass, indem es pro Schicht vier mögliche Engpässe berechnet:

  1. Rechenzeit der GPU: basierend auf FLOPs pro Schicht und der Hardwarerechenleistungslimit.
  2. Datentransferzeit: basierend auf den aus dem HBM geladenen Bytes pro Schicht und der Speicherbandbreite.
  3. Kommunikationszeit zwischen GPUs: basierend auf dem Kommunikationstyp (All-Reduce, All-Gather, All-to-All), Kommunikationsvolumen, Kommunikationsbandbreite und Netzwerktopologie.
  4. CPU-Overhead: fixer Kernel-Launch-Overhead pro Schicht plus globale Per-Forward-Pass-Overheads.

Für jede Schichtkomponente c{attn,mlp,embedding,lm_head}c \in \{\text{attn}, \text{mlp}, \text{embedding}, \text{lm\_head}\} berechnen sich Memory- und Compute-Zeiten als:

tmem,c=DcWmemηmem,c,tcompute,c=FcWcomputeηcompute,c,tbottleneck,c=max(tmem,c,tcompute,c)t_{\text{mem},c} = \frac{D_c}{W_{\text{mem}}}\,\eta_{\text{mem},c}, \qquad t_{\text{compute},c} = \frac{F_c}{W_{\text{compute}}}\,\eta_{\text{compute},c}, \qquad t_{\text{bottleneck},c} = \max(t_{\text{mem},c},\, t_{\text{compute},c})

wobei DcD_c die aus dem HBM geladenen Bytes, FcF_c die FLOPs und WmemW_{\text{mem}} und WcomputeW_{\text{compute}} die Speicherbandbreite und Peak-FLOPS der Hardware sind. Die η\eta-Faktoren erfassen die Lücke zwischen theoretischem Limit und dem, was Kernel tatsächlich erreichen. Sie modellieren statisch die Cache-Misses, Occupancy-Grenzen und Implementierungsineffizienzen, die die reale Geschwindigkeit drücken.

Die Kommunikationszeit hängt von der Art der kollektiven Operation und der verwendeten algorithmischen Implementierung ab:

tcomm=αVWcommηcommt_{\text{comm}} = \frac{\alpha \cdot V}{W_{\text{comm}}}\,\eta_{\text{comm}}

mit Datenvolumen VV, Kommunikationsbandbreite WcommW_{\text{comm}}, und einem algorithmusabhängigen Faktor α\alpha für nn GPUs:

Algorithmus All-Reduce All-Gather
Ring2(n1)/n2(n-1)/n(n1)/n(n-1)/n
Tree2log2n2\log_2 n
Butterflylog2n\log_2 n

Für Multi-Node-Setups mit heterogenen Netzwerken modelliert CHEETAH optional den Engpass des Gesamtnetzwerks (NVLink intra-node unidirektional 450900\sim450-900 GB/s; InfiniBand inter-node 50\sim50 GB/s9):

tcomm=αVηcommmax(nnodes1nnodes1Winter,1nnodes1Wintra).t_{\text{comm}} = \alpha \cdot V \cdot \eta_{\text{comm}} \cdot \max\left( \frac{n_{\text{nodes}}-1}{n_{\text{nodes}}} \cdot \frac{1}{W_{\text{inter}}}, \frac{1}{n_{\text{nodes}}} \cdot \frac{1}{W_{\text{intra}}} \right).

Je nach Sharding (Aufteilung der Gewichte auf verschiedene GPUs) wird Kommunikation überlappend oder seriell ausgeführt:

tlayer={max(tbottleneck,tcomm)U¨berlappen der Kommunikationtbottleneck+tcommsonst.t_{\text{layer}} = \begin{cases} \max(t_{\text{bottleneck}}, t_{\text{comm}}) & \text{Überlappen der Kommunikation}\\ t_{\text{bottleneck}} + t_{\text{comm}} & \text{sonst} \end{cases}.

Der vollständige Forward Pass summiert die Schichten und addiert die CPU-Overheads:

ttotal==1Ltlayer,+Lτlayer+τglobal+τbatchB.t_{\text{total}} = \sum_{\ell=1}^{L} t_{\text{layer},\ell} + L\,\tau_{\text{layer}} + \tau_{\text{global}} + \tau_{\text{batch}}\cdot B.

Dabei ist τlayer\tau_{\text{layer}} der Kernel-Launch-Overhead pro Schicht, τglobal\tau_{\text{global}} der globale Scheduling-Overhead und τbatchB\tau_{\text{batch}}\cdot B ein batch-größenabhängiger statischer Overhead. Aus ttotalt_{\text{total}} leiten wir die Durchsatz- und Latenzmetriken des Artikels ab. In der Regel können diese statischen Overheads vernachlässigt werden. Bei kleinen Modellen (unter 1B Parametern) können sie allerdings relevant werden.

Die Parallelisierung definiert, wie die Arbeit auf die GPUs aufgeteilt wird. Tensor Parallelität teilt Gewichtsmatrizen über GPUs hinweg auf, sodass FLOPs und Speicherzugriffe mit 1/TP1/\text{TP} skalieren. Dafür wird ein All-Reduce nach den row-parallelen Schichten gemacht. Data Parallelität repliziert die Gewichte und teilt den Batch auf, ohne Kommunikation während der Inferenz. Expert Parallelität verteilt die Experten eines MoE-Modells über die GPUs und nutzt eine All-to-All-Kommunikation zum Verteilen der Token und Zusammenführen der Resultate. Kombinationen aller drei werden ebenfalls unterstützt.

CHEETAH berechnet außerdem den größten Batch, der in den Speicher passt:

Bmax=MGPUMModelSMper-seq,B_{\text{max}} = \frac{M_{\text{GPU}} - M_{\text{Model}}}{S \cdot M_{\text{per-seq}}},

mit GPU-Speichergröße MGPUM_{\text{GPU}}, der Größe der Modellgewichte MModelM_{\text{Model}}, und Per-Sequenz-Speicheranforderung Mper-seqM_{\text{per-seq}} (KV-Cache und Aktivierungen). Überschreitet der angeforderte Batch BmaxB_{\text{max}}, läuft CHEETAH entweder mit der reduzierten Größe oder gibt eine Warnung aus.

Einschränkungen und Annahmen

CHEETAH ist ein theoretisches Modell. Es bildet die wichtigsten Inferenz-Engpässe ab, muss dabei aber bestimmte Annahmen treffen. Es setzt einen Steady-State voraus, das bedeutet eine feste Batch-Größe und Sequenzlänge, und modelliert daher keine Continuous-Batching-Dynamiken bei ankommenden und abgehenden Anfragen, keine variablen Sequenzlängen innerhalb eines Batches und keine Prefill-Decode-Interferenzen bei Co-Location. Die Ineffizienzfaktoren werden einmal aus Messungen kalibriert und dann konstant gehalten, um Overfitting zu vermeiden, obwohl die reale Effizienz mit Batch-Größe und Sequenzlänge variiert. Wir verwenden separate η\eta für Attention, MLP und für Speicherbandbreite, Compute und Kommunikation. Dies sind aber auch nur Schätzungen. Kommunikation wird als rein bandbreitenbegrenzt modelliert; latenzgebundene Kommunikationsfälle, wie sie bei kleinen Modellen auftreten können, werden daher unterschätzt.

Validierung

Zur Validierung haben wir Messungen zur Inferenzgeschwindigkeit von DeepSeek v3 gesammelt und die Ineffizienzfaktoren einmal pro Modell kalibriert (konstant über die verschiedenen Setups, Batch-Größen und Sequenzlängen). Als Basis dienen bekannte Kernel-Charakteristika, sofern diese öffentlich sind (FlashMLA erreicht etwa 0,66 MFU, DeepGEMM 1550 von theoretisch möglichen 1980 TFLOPS, DeepEP rund 40 GB/s über InfiniBand und höher über gemischte Netzwerke). Die Optimierung erfolgt mit bayessche Optimierung.

DeepSeek v3 ist ein guter Testfall für CHEETAH: Es ist ein MoE-Modell, läuft in großem Maßstab (dadurch sind statische Overheads klein, das Steady-State-Modell ist korrekt), und es gibt mehr öffentliche Messungen zur Inferenzgeschwindigkeit als für fast jedes andere Modell. Zudem vergleichen wir mit Messungen, die wir selbst für kleinere Setups erzeugt haben (2 und 4 H100 SXM Nodes, auf SGLang mit DeepEP und DeepGEMM; Setup Instruktionen) und mit publizierten Messungen von SGLang für große Setups (large-scale EP, GB200 NVL72).

Abbildung 1 am Anfang dieses Blogposts zeigt den Durchsatz pro Anfrage über diesen Bereich; Abbildung 10 schlüsselt den gleichen Vergleich pro GPU auf und zeigt, wie unterschiedliche effizient die Setups sein können.

CHEETAH compared to measured baselines on DeepSeek v3
Abbildung 10: Gleicher Vergleich wie Abbildung 1, aber pro GPU, über vier Hardware- und Parallelisierungssetups bei Sequenzlänge 2048. Die Effizienz wächst bei den großen Setups weiter mit der Batch-Größe, während die kleineren ein Plateau erreichen, sobald der Speicher voll ist. Das erlaubt Vergleiche von Setups, die wir noch nicht implementiert haben oder für die wir keinen Hardware-Zugang haben.

CHEETAH bildet den gemessenen Durchsatz über drei Größenordnungen von Batch-Größen gut nach. Noch besser: Es sagt korrekt vorher, wo sich der Engpass ändert. Das macht es für die Erkundung des Design-Raumes nützlich. Die absoluten Zahlen sind eine theoretische Obergrenze. Aber die relativen Vergleiche zwischen Architekturen und Setups sind konsistent geordnet.

Danksagungen

Ein besonderer Dank gilt Piotr Mazurek. Die Idee, Inferenz-Engpässe von Grund auf zu betrachten, stammt von ihm. Gemeinsam haben wir sie bei Aleph Alpha erstmals im Theoretischen Inferenz-Modell für DeepSeek umgesetzt. Viele der Plots in diesem Beitrag gehen auf seine Ideen zurück, was gezeigt werden soll und wie, und er hat mir den Anstoß gegeben, über diese Themen zu schreiben. Seine Publikation Tensor Economics vertieft diese Themen weiter.

Wir danken Steffen Hirschmann und Letiția Pârcălăbescu für ihre sorgfältigen Reviews sowie Yasser Jadidi, Fabien Benureau, Jordan Sassoon, Riccardo Mereu und Paul Chang für ihr wertvolles Feedback, das diesen Blogpost mitgeprägt hat. Unser Dank gilt außerdem Noé Beckerle Vallejo und Alexander Wortmeier, die ihn zum Leben erweckt haben, und Svenja Fahlisch, die dafür gesorgt hat, dass alles reibungslos lief.

Fußnoten

  1. Ein Roofline-Diagramm zeigt die Geschwindigkeitsobergrenze einer Berechnung auf einer bestimmten GPU. Die x-Achse zeigt die arithmetische Intensität (FLOPs pro Byte Datentransfer), die y-Achse die erreichbare Rechenleistung (FLOPS/s). Eine Berechnung kann entweder compute- oder memory-bound sein, zwei Grenzen, die das „Dach“ bilden: die Speicherbandbreite (Diagonale) und die Rechengrenze (horizontale Linie).
  2. Als Beispiel: eine allgemeine Matrixmultiplikation A×BA \times B, mit AA in Form M×KM \times K, BB in Form K×NK \times N und bb Byte pro Element (die numerischen Präzision). Wir bewegen beide Eingaben und die Ausgabe der Operation,
    bewegte Bytes=b(KN+MK+MN),\text{bewegte Bytes} = b\,(KN + MK + MN),
    und führen für jedes der MKNMKN elementweisen Produkte eine Multiplikation und eine Addition aus, daher
    FLOPs=2MKN.\text{FLOPs} = 2MKN.
    Die arithmetische Intensität ist das Verhältnis der beiden,
    I=2MKNb(KN+MK+MN)=2b11M+1N+1K.I = \frac{2MKN}{b\,(KN + MK + MN)} = \frac{2}{b} \cdot \frac{1}{\frac{1}{M} + \frac{1}{N} + \frac{1}{K}}.
    Benutzen wir die Formel für ein Beispiel: Nutzen wir AA als Eingabe, die mit den Gewichten des Modells BB multipliziert wird. MM ist dabei die Batch-Größe, im Decode also die Zahl der laufenden Sequenzen. Bei einer einzelnen Sequenz in BF16 (b=2b = 2) liefert die Formel
    I=22111+128672+181921,I = \frac{2}{2} \cdot \frac{1}{\frac{1}{1} + \frac{1}{28672} + \frac{1}{8192}} \approx 1,
    wobei KK und NN mit Blick auf Llama 3.1 70B gewählt sind. Da die arithmetische Intensität kleiner ist als der Ridge Point, ist die Operation memory-bound. Im Prefill können wir alle Token auf einmal berechnen, sodass eine einzelne Sequenz von etwa 512 Token eine effektive Batch-Größe MM von 512 hat und damit eine arithmetische Intensität von
    I=2211512+128672+18192474.I = \frac{2}{2} \cdot \frac{1}{\frac{1}{512} + \frac{1}{28672} + \frac{1}{8192}} \approx 474.
    Dies ist ein einfaches Beispiel einer einzelnen Operations. Für Abbildung 2 berechnen wir die arithmetische Intensität für alle Operationen eines Modells.
  3. Diese Aufteilung hängt auch stark von der Modellarchitektur ab. Ein kleinerer KV-Cache (z. B. durch Grouped-Query oder Multi-Head Latent Attention) beschleunigt hauptsächlich Decode und erhöht damit den Prefill-Anteil an der Gesamtzeit.
  4. In der Praxis werden mehrere Forward Passes durchgeführt, als sogenanntes „Chunked Prefill“ (vLLM Doku). Der Einfachheit halber ignorieren wir das hier.
  5. Tensor Cores sind dedizierte Hardwareeinheiten in NVIDIA-GPUs, die kleine Matrixmultiplikationen berechnen. Sie liefern den Großteil der beworbenen FLOPS einer GPU.
  6. Die Inference-Engine (z. B. vLLM, SGLang, TensorRT-LLM) ist der Server zwischen eingehenden Anfragen und den GPUs. Statt einen fixen Batch bis zum Ende durchzuführen, hat sie einen laufenden Batch und lässt neue Anfragen eintreten, sobald andere fertig sind (Continuous Batching). Zudem entscheidet sie bei jedem Schritt, ob ein Prefill-Chunk oder Decode-Schritt ausgeführt wird.
  7. Beide Inferenzphasen auf den gleichen GPUs zu betreiben (Co-Location) ist das einfachste Setup. Da Prefill und Decode aber verschiedene Engpässe haben, will man das Setup für jede Phase separat optimieren. Bei Prefill-Decode-Disaggregation werden beide Phasen auf getrennten GPU-Pools betrieben und der KV-Cache zwischen ihnen übertragen, sodass jeder Pool unabhängig dimensioniert und optimiert werden kann (Dokumentation von vLLM).
  8. Speculative decoding nutzt ein kleines Draft-Modell, um mehrere zukünftige Token auf einmal vorzuschlagen, die das vollständige Modell im nächsten Forward Pass verifiziert. Da viele Token gemeinsam geprüft werden, laufen die Operationen mit höheren Batch-Größen und kommen so näher an das Compute-Bound-Regime.
  9. Eine DGX H100 Node enthält acht H100, voll verbunden durch NVLink der vierten Generation mit 450 GB/s pro GPU unidirektionaler Bandbreite (900 GB/s bidirektional). Für Inter-Node-Kommunikation hat jede Node acht ConnectX-7-NDR-InfiniBand-Adapter mit je 50 GB/s (~400 GB/s Node-Injektionsbandbreite; White Paper der Hopper-Architektur). Eine DGX B200 verdoppelt den Intra-Node-Link auf 900 GB/s pro GPU (1,8 TB/s bidirektional) bei gleichem InfiniBand-Fabric (DGX B200 Datenblatt).