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.
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
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
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 und für die Attention ( und ) mit . Bei kurzen Sequenzen dominieren die linearen Projektionen (also Matrix-Multiplikationen), doch bei langen Sequenzen überwiegt der quadratische Attention-Anteil deutlich (Abbildung 4).
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 identische Prefill-Anfragen, jede lang genug, dass fixe Overheads vernachlässigbar sind. Das Prefilling einer einzelnen Anfrage dauert . Da wir compute-bound sind, skaliert die Zeit linear mit , 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 :
Eindeutig 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 . 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:
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).
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.
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):
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.
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.
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:
- Rechenzeit der GPU: basierend auf FLOPs pro Schicht und der Hardwarerechenleistungslimit.
- Datentransferzeit: basierend auf den aus dem HBM geladenen Bytes pro Schicht und der Speicherbandbreite.
- Kommunikationszeit zwischen GPUs: basierend auf dem Kommunikationstyp (All-Reduce, All-Gather, All-to-All), Kommunikationsvolumen, Kommunikationsbandbreite und Netzwerktopologie.
- CPU-Overhead: fixer Kernel-Launch-Overhead pro Schicht plus globale Per-Forward-Pass-Overheads.
Für jede Schichtkomponente berechnen sich Memory- und Compute-Zeiten als:
wobei die aus dem HBM geladenen Bytes, die FLOPs und und die Speicherbandbreite und Peak-FLOPS der Hardware sind. Die -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:
mit Datenvolumen , Kommunikationsbandbreite , und einem algorithmusabhängigen Faktor für GPUs:
| Algorithmus | All-Reduce | All-Gather |
|---|---|---|
| Ring | ||
| Tree | — | |
| Butterfly | — |
Für Multi-Node-Setups mit heterogenen Netzwerken modelliert CHEETAH optional den Engpass des Gesamtnetzwerks (NVLink intra-node unidirektional GB/s; InfiniBand inter-node GB/s9):
Je nach Sharding (Aufteilung der Gewichte auf verschiedene GPUs) wird Kommunikation überlappend oder seriell ausgeführt:
Der vollständige Forward Pass summiert die Schichten und addiert die CPU-Overheads:
Dabei ist der Kernel-Launch-Overhead pro Schicht, der globale Scheduling-Overhead und ein batch-größenabhängiger statischer Overhead. Aus 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 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:
mit GPU-Speichergröße , der Größe der Modellgewichte , und Per-Sequenz-Speicheranforderung (KV-Cache und Aktivierungen). Überschreitet der angeforderte Batch , 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 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 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
- 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). ↩
-
Als Beispiel: eine allgemeine Matrixmultiplikation ,
mit
in Form , in Form
und Byte pro Element (die numerischen
Präzision). Wir bewegen beide Eingaben und die Ausgabe der Operation,
und führen für jedes der elementweisen Produkte eine Multiplikation und eine Addition aus, daherDie arithmetische Intensität ist das Verhältnis der beiden,Benutzen wir die Formel für ein Beispiel: Nutzen wir als Eingabe, die mit den Gewichten des Modells multipliziert wird. ist dabei die Batch-Größe, im Decode also die Zahl der laufenden Sequenzen. Bei einer einzelnen Sequenz in BF16 () liefert die Formelwobei und 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 von 512 hat und damit eine arithmetische Intensität vonDies ist ein einfaches Beispiel einer einzelnen Operations. Für Abbildung 2 berechnen wir die arithmetische Intensität für alle Operationen eines Modells. ↩
- 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. ↩
- In der Praxis werden mehrere Forward Passes durchgeführt, als sogenanntes „Chunked Prefill“ (vLLM Doku). Der Einfachheit halber ignorieren wir das hier. ↩
- Tensor Cores sind dedizierte Hardwareeinheiten in NVIDIA-GPUs, die kleine Matrixmultiplikationen berechnen. Sie liefern den Großteil der beworbenen FLOPS einer GPU. ↩
- 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. ↩
- 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). ↩
- 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. ↩
- 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). ↩