DeepGEMM: Saubere und effiziente FP8-GEMM-Kernel durch feingranulares Scaling
(github.com/deepseek-ai)- DeepGEMM ist eine Hochleistungsbibliothek für Tensor-Core-Kernel, die GEMM, fused MoE, MQA-Scoring, HyperConnection und weitere zentrale Rechenprimitiven moderner LLMs in einer einzigen CUDA-Codebasis bündelt
- Alle Kernel werden zur Laufzeit als leichtgewichtige JIT-Module kompiliert; eine CUDA-Kompilierung während der Installation ist nicht nötig, vorausgesetzt werden C++20, CUDA Toolkit, PyTorch und CUTLASS 4.0 oder neuer
- Es nutzt einige Konzepte aus CUTLASS und CuTe, stützt sich jedoch nicht stark auf Templates oder algebraische Strukturen und ist mit einer begrenzten Zahl zentraler Kernelfunktionen so gestaltet, dass der Einstieg in die NVIDIA-GPU-Kerneloptimierung leichter fällt
- Der unterstützte Umfang umfasst FP8-, FP4- und BF16-GEMM, grouped GEMM, MQA-Logits-Kernel für DeepSeek v3.2 sowie Mega MoE, das Kommunikation und Berechnung überlappt; dabei gelten auf SM90 und SM100 unterschiedliche Speicherlayout-Beschränkungen
- Trotz des schlanken Designs zielt es bei vielen Matrix-Shapes auf gleiche oder bessere Leistung als spezialisierte, manuell abgestimmte Bibliotheken und enthält ein Update, das auf H800 bis zu 1550 TFLOPS erreicht
Zweck und Design von DeepGEMM
- DeepGEMM ist eine Bibliothek für Tensor-Core-Kernel, die die wichtigsten Rechenprimitiven aktueller großer Sprachmodelle in einer einzigen CUDA-Codebasis zusammenführt
- GEMM: FP8, FP4, BF16
- fused MoE mit überlappter Kommunikation: Mega MoE
- MQA-Scoring für den lightning indexer
- HyperConnection (HC)
- Alle Kernel werden zur Laufzeit als leichtgewichtige Just-In-Time(JIT)-Module kompiliert
- Während der Installation ist keine CUDA-Kompilierung erforderlich
- Es verwendet einige Konzepte aus CUTLASS und CuTe
- Stützt sich jedoch nicht stark auf schwere Template- oder algebraische Strukturen
- Hält die Codebasis einfach, indem die Zahl zentraler Kernelfunktionen begrenzt wird
- Trotz des leichten Designs wird angegeben, bei verschiedenen Matrix-Shapes gleichwertige oder bessere Leistung als spezialisierten, manuell abgestimmten Bibliotheken zu liefern
Wichtige Updates
- Das Update vom 16. April 2026 umfasst Mega MoE, FP8xFP4-GEMM, FP4 Indexer, PDL und schnellere JIT-Kompilierung
- Das Update vom 28. September 2025 ergänzt einen Scoring-Kernel für weighted ReLU MQA logits für den DeepSeek-v3.2-lightning-indexer
- Details unter #200
- Das Update vom 20. Juli 2025 unterstützt sowohl SM90 als auch SM100 und wurde vollständig auf ein JIT-CPP-Modul mit niedrigem CPU-Overhead refaktoriert
- NVRTC und die SASS-Optimierung nach der Kompilierung sind deaktiviert
- NVRTC ist als Unterstützung für später vorgesehen
- Da NVCC 12.9 FFMA-Interleaving automatisch ausführt, wird die Optimierung nach der Kompilierung nicht mehr unterstützt
- Details unter #112
- Das Update vom 14. Mai 2025 fügt weight gradient-Kernel für dense und MoE backward hinzu
- Details unter #95
- Das Update vom 7. Mai 2025 bietet mit NVRTC-Unterstützung bis zu 10-fach schnellere Kompilierung
- Aktivierbar mit
DG_JIT_USE_NVRTC=1 - In einigen Fällen kann es zu Leistungsverlusten kommen
- Details unter #94
- Aktivierbar mit
- Das Update vom 18. April 2025 erreicht auf H800 bis zu 1550 TFLOPS
Anforderungen und Installationsablauf
- Die Laufzeitumgebung erfordert eine GPU mit NVIDIA-SM90- oder SM100-Architektur
- Die Softwareanforderungen sind wie folgt
- Python 3.8 oder neuer
- Compiler mit C++20-Unterstützung
- CUDA Toolkit
- SM90: CUDA 12.3 oder neuer
- Für Spitzenleistung wird CUDA 12.9 oder neuer dringend empfohlen
- SM100: CUDA 12.9 oder neuer
- PyTorch 2.1 oder neuer
- CUTLASS 4.0 oder neuer
{fmt}-Bibliothek
- In einer Entwicklungsumgebung wird das Repository einschließlich Submodulen geklont und anschließend mit
develop.shdie notwendigen Includes verknüpft und das CPP-JIT-Modul gebaut - Die Installation erfolgt durch Ausführen von
install.sh; danach wirddeep_gemmim Python-Projekt importiert
GEMM-Schnittstelle und Layout-Beschränkungen
- Die Benennungskonvention der GEMM-Kernel in DeepGEMM lautet
D = C + A @ B - Das Layout der Eingabe-Shapes basiert auf NT
fp8_gemm_ntführtD = C + A @ B.Taus
- Die SM90-Implementierung unterstützt nur das NT-Speicherlayout
- Entspricht einer Kombination aus row-major und col-major
- Die SM100-Implementierung unterstützt alle Speicherlayouts NT, TN, NN, TT
- Auf beiden Architekturen muss der LHS-Scaling-Faktor ein TMA-ausgerichtetes und transponiertes Layout haben
- SM90 verlangt den Scaling-Faktor im FP32-Format
- SM100 verlangt das gepackte UE8M0-Format, bei dem vier UE8M0 in ein
torch.intgepackt werden
- Aufgaben wie Eingabetransposition oder FP8-Casting müssen vom Nutzer separat erledigt werden
- Die Bibliothek stellt einfache PyTorch-Hilfsfunktionen bereit, diese können jedoch langsam sein
- Der Hauptfokus liegt auf der Optimierung der GEMM-Kernel
Dense und Grouped GEMM
- Für grundlegendes nicht gruppiertes FP8-GEMM werden die Funktionen
fp8_gemm_{nt, nn, tn, tt}verwendet - Grouped GEMM mit contiguous layout gruppiert im Unterschied zum klassischen grouped GEMM von CUTLASS nur entlang der M-Achse
- N und K müssen fest sein
- Das Design ist auf Situationen in MoE-Modellen zugeschnitten, in denen Experten dieselbe Shape teilen
- Im Training-Forward-Pass oder beim Inference-Prefilling kann die Zahl der pro Experte verarbeiteten Tokens variieren
- Die Form, bei der solche Tokens in einem einzigen Tensor aneinandergereiht werden, wird als contiguous layout bezeichnet
- Jedes Expert-Segment muss auf die M-Blockgröße des GEMM ausgerichtet sein
- Die Ausrichtungsanforderung lässt sich mit
get_mk_alignment_for_contiguous_layout()prüfen
- Für das Weight-Backward von MoE steht auch eine entlang der K-Achse gruppierte API bereit
- M und N müssen fest sein
- Relevante Funktion:
k_grouped_fp8_gemm_tn_contiguous
- In der Inference-Decoding-Phase wird masked grouped GEMM unterstützt, wenn CUDA graph aktiviert ist und die CPU die Token-Zahl pro Experte nicht kennt
- Wird ein Mask-Tensor bereitgestellt, berechnet der Kernel nur gültige Bereiche
- Die Funktion lautet
m_grouped_fp8_gemm_nt_masked - Es gibt ein Beispiel, das die Ausgabe des Low-Latency-Kernels von DeepEP als Eingabe verwendet
MQA-Kernel für den DeepSeek-v3.2-Indexer
- Die MQA-Kernel-Familie für V3.2 bietet eine non-paged- und eine paged-Version
- non-paged ist für Prefilling
- paged ist für Decoding
fp8_mqa_logitserhält 6 Eingabenq: E4M3-Tensor, Shape[seq_len, num_heads, head_dim]kv: E4M3-Tensor und Float-Scaling-Faktor- Die Tensor-Shape ist
[seq_len_kv, head_dim] - Die Shape des Scaling-Faktors ist
[seq_len_kv]
- Die Tensor-Shape ist
weights: Float-Tensor, Shape[seq_len, num_heads]cu_seq_len_k_start,cu_seq_len_k_end: Int-Tensoren, Shape[seq_len]clean_logits: Ob nicht belegte Logits auf-infbereinigt werden sollen
- Die Ausgabe-Tensor-Shape ist
[seq_len, seq_len_kv]und stellt Token-zu-Token-Logits dar - Jedes
q-Tokenidurchläuftkv-Tokenjvoncu_seq_len_k_start[i]bis vorcu_seq_len_k_end[i]- Auf
kv_jwird der Scaling-Faktor angewendet - Mit
q[i, :, :] @ kv_jwerden Werte pro Head berechnet - Nach Anwendung von ReLU werden sie mit
weights[i, :]multipliziert und aufsummiert, um einen skalaren Logit zu erzeugen
- Auf
- Die Funktion der paged-Version heißt
fp8_paged_mqa_logits
Mega MoE
- Mega MoE fusioniert mehrere MoE-Stufen in einem einzigen Mega-Kernel
- EP dispatch
- linear 1, FP8xFP4
- SwiGLU
- linear 2, FP8xFP4
- EP combine
- Mega MoE überlappt NVLink-Kommunikation und Tensor-Core-Berechnung
- Für die Ausführung ist ein Multi-Process-Start mit symmetric memory erforderlich
- Der Nutzungsablauf ist wie folgt
- Mit
deep_gemm.get_symm_buffer_for_mega_moewird ein symmetric-memory-Puffer allokiert- Erfordert PyTorch 2.9 oder neuer
- Mit
deep_gemm.transform_weights_for_mega_moewerden Gewichte einschließlich FP4 und UE8M0 SF in das benötigte Layout transformiert - Vor dem Aufruf werden Eingaben, Scaling-Faktor, Top-k-Index und Top-k-Gewicht in den Puffer kopiert
- Mit
deep_gemm.fp8_fp4_mega_moewird der fused-Mega-MoE-Kernel ausgeführt
- Mit
- Ein vollständiges Beispiel für Multi-Process-Setup und Benchmarking findet sich in
tests/test_mega_moe.py
Hilfsfunktionen und Umgebungsvariablen
- Wichtige Hilfsfunktionen steuern Laufzeitressourcen, Alignment, JIT-Kompilierung und die Umwandlung von Scaling-Faktoren
deep_gemm.set_num_sms/get_num_sms: Maximale Zahl der zu verwendenden SMs setzen und abfragendeep_gemm.set_tc_util/get_tc_util: Näherungswert für das Tensor-Core-Utilization-Verhältnis setzen und abfragendeep_gemm.set_pdl/get_pdl: Programmatic Dependent Launch (PDL) aktivieren und deaktivierendeep_gemm.set_mk_alignment_for_contiguous_layout/get_mk_alignment_for_contiguous_layout: M/K-Alignment auf Gruppenebene für contiguous layout setzen und abfragendeep_gemm.transform_sf_into_required_layout: Scaling-Faktor in das erforderliche Layout transformierendeep_gemm.get_tma_aligned_size: Erforderliche TMA-Alignment-Größe abfragen
- JIT-bezogene Umgebungsvariablen steuern Debug-Ausgabe, Cache-Speicherort, Compiler-Auswahl und Profiling-Optionen
DG_JIT_DEBUG: Gibt JIT-Debug-Informationen ausDG_PRINT_CONFIGS: Gibt die ausgewählte Konfiguration pro Shape ausDG_JIT_CACHE_DIR: Cache-Verzeichnis für kompilierte Kernel, Standard ist$HOME/.deep_gemmDG_JIT_USE_NVRTC: Verwendet NVRTC statt NVCC; schnellere Kompilierung möglich, aber in manchen Fällen geringere LeistungDG_JIT_NVCC_COMPILER: Pfad zum NVCC-CompilerDG_JIT_CPP_STANDARD: Version des C++-Standards, Standardwert ist20
- Es gibt auch Umgebungsvariablen für Debugging und Profiling
DG_JIT_DUMP_ASM,DG_JIT_DUMP_PTX,DG_JIT_DUMP_SASS: Dumps der PTX- und SASS-AusgabeDG_JIT_WITH_LINEINFO: Enthält Source-Line-Informationen für Profiling-ToolsDG_COMM_KERNEL_DEBUG: Initialisiert den symmetric buffer vor Mega-MoE-Aufrufen mit 0DG_USE_NVIDIA_TOOLS: Überspringt internes Profiling beim Einsatz externer NVIDIA-Tools
- Build-Optionen steuern Installation und Kernel-Ladeverfahren
DG_SKIP_CUDA_BUILD: Überspringt den Build der CUDA-Erweiterung während der InstallationDG_FORCE_BUILD: Erzwingt einen lokalen Build statt des Downloads eines vorgefertigten WheelsDG_JIT_USE_RUNTIME_API: Verwendet die CUDA Runtime API zum Laden von Kerneln; benötigt CUDA Runtime 12.8 oder neuer
Lizenz und Zitation
- Das DeepGEMM-Repository steht unter der MIT License
- Das Projekt gibt an, von CUTLASS inspiriert zu sein
- Der Titel des Zitierhinweises lautet
DeepGEMM: clean and efficient BLAS kernel library on GPU
1 Kommentare
Meinungen auf Hacker News
FFMA-SASS-Interleaving sieht wirklich erstaunlich aus.
Offenbar bemerkte man, dass die Performance der CUTLASS-FP8-Kernel zwischen NVCC 12.2 und 12.3 besser wurde, verglich das kompilierte SASS und fand heraus, dass bei mehreren FADD-Instruktionen ein Bit in einem Interleaving-Muster umgeschaltet war. Unter Bezug auf eine Open-Source-Implementierung eines CUDA-Assemblers erkannte man wohl, dass dieses Bit das Yield-Bit ist, das den aktuellen Warp abgibt, damit ein anderer Warp ausgeführt werden kann.
Beeindruckend ist, dass daraus ein Skript entstand, das die FFMA-Instruktionen im kompilierten Binary patcht, und dabei nicht nur das Yield-Bit, sondern auch das Reuse-Bit umschaltet, weil Register-Reuse nicht möglich ist, wenn ein Warp abgibt. So lassen sich bei fein skaliertem FP8-GEMM MMA-Instruktionen und Promotion-FFMA-Instruktionen besser überlappen, was in manchen Fällen die Performance um mehr als 10 % steigert.
Nur scheinen andere AI-Unternehmen bei diesem konkreten Problem die Notwendigkeit noch nicht gesehen und es deshalb nicht angewendet zu haben; am Ende werden aber vermutlich alle an einem ähnlichen Punkt landen.
Solche Beispiele zeigen sehr gut, wie weit heutige Compiler davon entfernt sind, allein aus High-Level-Code die Hardware-Performance herauszuholen.
Ich frage mich, was nötig wäre, damit klassische Compiler-Techniken oder KI-basierte Optimierungsagenten solche Ergebnisse liefern.
Die gemeldeten Speedup-Zahlen werden mit einer eigenen CUTLASS-basierten Baseline verglichen.
Ich frage mich, ob jemand einen direkten Performancevergleich mit cuBLAS gemacht hat.
Die CUTLASS-GEMM-Ergebnisse, die ich bisher gesehen habe, lagen gegenüber cuBLAS ungefähr innerhalb von 10 %. Wenn die im Paper genannten 2x bis 2,5x Verbesserungen erhalten bleiben, wäre das wirklich beeindruckend.
Zunächst braucht cuBLAS für Mixed-Precision-Operationen wie FP8 die Erweiterungs-API cuBLASLt.
Außerdem werden naheliegend wirkende Typkombinationen wie E5M2 x E5M2 bei A x B nicht unterstützt, E5M2 x E4M3 dagegen schon; zudem bestehen weitere Einschränkungen, etwa dass auf Ampere, Hopper und Blackwell Matrix A immer in transponiertem Layout vorliegen muss.
Ich habe den FP8-cuBLASLt-Benchmark in mein „Less Slow C++“-Repository <https://github.com/ashvardanian/less_slow.cpp> integriert und ihn der Liste bestehender cuBLAS- sowie selbst geschriebener CUDA/PTX-Benchmarks hinzugefügt.
Ich lasse ihn auf einer H200-GPU laufen, die dieselbe Performance wie eine H100 haben sollte; bei quadratischen Eingaben erreicht der Durchsatz einen Peak von etwa 1,35 Peta-ops.
256 liegt bei 2,68 T/s, 512 bei 20,49 T/s, 1024 bei 144,23 T/s, 2048 bei 665,68 T/s, 4096 bei 1,26 P/s, 8192 bei 1,34 P/s und 16384 bei etwa 1,23 P/s; das entspricht ungefähr 67 % der von NVIDIA für dichtes GEMM beworbenen Zahl <https://resources.nvidia.com/en-us-data-center-overview-mc/e...>.
Ich dachte, als Baseline habe man die bessere Variante aus cuBLAS und CUTLASS gewählt.
Solche Open-Source-Projekte zeigen wirklich gut das Ziel der Branche, Effizienz zu erreichen.
Allerdings dürften die Nutznießer dieser Software eher große Anbieter sein, die Modelle in großem Maßstab serven – also potenzielle Konkurrenten von DeepSeek – und weniger die allgemeine Open-Source-Community, die lernen, experimentieren oder Modelle auf Consumer-Hardware bereitstellen möchte.
Ich bin mir nicht sicher, ob die langfristige Richtung, immer stärker auf niedrigere Präzision zu optimieren, wirklich gut ist.
Das bedeutet, dass Modelle tatsächlich ziemlich sparse sind; derzeit mag das stimmen, aber wahrscheinlich eher, weil in der Trainingsmethode schlechte Ideen stecken, und nicht, weil sie ihrem Wesen nach so sparse sein müssten.
Zu erreichen, dass man sehr gute Modelle nur mit höherer Präzision trainieren kann, ist ein Forschungsproblem; Low-Precision-Training und -Inference sind Engineering-Probleme.
Seit den CNN-Zeiten, mindestens seit neun Jahren, machen wir solche Dinge, und ich denke, ein paar Jahre bleiben noch.
Das könnte durch MXFP, Blackwells native Microscaling-Unterstützung, bedeutungslos werden.
Auf Hopper hat man das im Grunde manuell mit gröberer Granularität, dafür mit FP32-Skalierungskoeffizienten umgesetzt.
Solche hochwertigen öffentlichen Demonstrationen zeigen sehr gut, wo der Burggraben von $NVDA liegt.
Universal-GPUs sind sehr flexibel: Man kann per Programmierung viele Aufgaben ausführen, die Hardwareanbieter anfangs nicht bedacht haben, die aber durchaus sinnvoll sind.
Wenn man allerdings vorhersagt, dass die Zukunft immer stärker in Richtung dedizierter Hardwareunterstützung konvergiert und damit der Spielraum für solche Softwareoptimierungen verschwindet, dann bricht der sogenannte CUDA-Burggraben zusammen.
Um im Spiel zu bleiben, reißt NVIDIA also den eigenen Burggraben selbst ein :p
Wow, es steht unter der MIT-Lizenz.
Ich wünschte, große Anbieter würden diese Art von Open-Source-Zusammenarbeit übernehmen.
Ich frage mich weiterhin, warum es undokumentierte Befehle gibt
Auch wenn sie nicht vollkommen stabil sind, fände ich es richtig, sie den Nutzern bereitzustellen
Solche Dinge dürften intern dokumentiert sein, aber ich verstehe nicht, warum man sie nicht öffentlich macht
Security by obscurity funktioniert nicht, und die Konkurrenz betreibt ohnehin Reverse Engineering für alles
Es kann an Zeitmangel liegen oder daran, dass man für instabile oder experimentelle Funktionen keinen Support implizieren möchte
Wenn sich der Schaden nur auf das Team am Nachbartisch beschränkt, ist es auch viel einfacher, etwas zu ändern
Wahrscheinlich steht es höchstens in Architektur-Design-Dokumenten oder Spezifikationen, und solche Dokumente will man natürlich nicht teilen
Ehrlich gesagt liegt das außerhalb meines Nutzungsbereichs und Verständnisses
Trotzdem ist es wirklich erfreulich und erfrischend, dass solche Entdeckungen und Verbesserungen geteilt werden, damit alle davon profitieren können
Er ist für Matrixmultiplikation und Deep-Learning-Workloads sehr wichtig
In NVIDIAs SASS werden FFMA-Befehle als 64-Bit- oder 128-Bit-Befehle kodiert und besitzen mehrere Kontrollbits, die ihr genaues Verhalten bestimmen
Wenn das yield-Bit gesetzt ist, signalisiert es dem Warp-Scheduler, dass der aktuelle Warp nach diesem Befehl die Ausführung abgeben kann, und die Hardware kann einen anderen Warp ausführen, um Latenzen zu verbergen
GPUs erreichen durch massive Parallelität einen hohen Durchsatz, und wenn ein Warp etwa wegen Speicherzugriffen wartet, kann ein anderer Warp weiterlaufen
Das reuse-Bit gibt an, ob Quellregister in der unmittelbar folgenden Operation wiederverwendet werden können; wenn das yield-Bit gesetzt ist, muss es zwingend ausgeschaltet sein
Denn wenn ein Warp abgibt, ist er möglicherweise nicht der nächste, der ausgeführt wird, und ein anderer Warp kann den Zustand der Registerdatei verändern, sodass die Hardware nicht garantieren kann, dass Registerwerte über ein yield hinweg erhalten bleiben
Wenn man die yield-Bits bei FFMA-Befehlen in einem alternierenden Muster setzt, erzeugt der Compiler explizite Scheduling-Punkte, an denen andere Warps vorankommen können; um die Korrektheit zu wahren, müssen dabei auch die reuse-Bits dieser Befehle gelöscht werden
Diese Änderung hilft besonders dabei, die für Matrixmultiplikation zentralen MMA-Befehle mit den Promotion-FFMA-Befehlen zu überlappen, die FP8 zur Akkumulation in eine höhere Präzision umwandeln
FP8-GEMM erfordert üblicherweise, Werte für die Akkumulation in eine höhere Präzision umzuwandeln und anschließend wieder zurückzuwandeln, wodurch zusätzliche FFMA entstehen; das senkt zwar die Anforderungen an die Speicherbandbreite, erzeugt aber ein komplexes Berechnungsmuster mit gemischten Promotion- und Demotion-Operationen
„Feingranulares Skalieren“ scheint die sorgfältige Verwaltung der Präzision an mehreren Stellen der Berechnung zu bedeuten
Die Manipulation des yield-Bits sorgt dafür, dass Rechenoperationen und Formatkonvertierungen besser interleaved werden, wodurch die GPU-Ausführungseinheiten effizienter genutzt werden; ohne diese Optimierung findet der Warp-Scheduler möglicherweise keine natürlichen Umschaltgelegenheiten, sodass Rechenressourcen schlechter ausgelastet werden