- Der Attention-Kernel wurde in eine persistente Form refaktoriert, was die Performance bei kurzen Context-Längen verbessert
- Bei fp16 tritt bei großen Contexts ein Performance-Einbruch durch ein ptxas-Problem beim Instruction Scheduling der Softmax-Partition auf
- Bei fp8 wurde beobachtet, dass das Einfügen von "cutlass" in den Kernel-Namen die Leistung um etwa 100 TFLOPS erhöht
- Diese Optimierung wird als Hardcoding-Optimierungstrick über die Kernel-Benennung analysiert
- Die Auswirkung auf die Performance und die Ursache des Verhaltens sind ein ungewöhnliches Phänomen aufgrund der internen Implementierung von NVIDIA ptxas
Zusammenfassung: Refaktorisierung des Persistent-Attention-Kernels und der Namenseffekt von "cutlass"
Überblick
- Dieser Pull Request refaktoriert den Attention-Kernel von Triton in Richtung Persistent Attention
- Das Hauptziel ist eine Performance-Optimierung im Bereich kurzer Context-Längen
- Allerdings zeigt sich beim Typ fp16, dass mit wachsender Context-Größe ein Performance-Rückgang durch ein ptxas-Problem beim Instruction Scheduling im Softmax-Teil auftritt
Effekt der Verwendung des Namens "cutlass" bei fp8
- Bei Verwendung von fp8-Modellen wurde gemessen, dass das Einfügen von "cutlass" in den Kernel-Namen die Leistung um bis zu 100 TFLOPS steigert
- Das liegt daran, dass der ptxas (PTX-Assembler) von NVIDIA intern eine spezielle Optimierung anwendet, wenn der Kernel-Name "cutlass" enthält
- Im tatsächlichen Code wird dem Kernel bei
float8e5 als dtype ein Name wie cutlass_gluon_attention zugewiesen
- Die Analyse der ptxas-Disassemblierung zeigt, dass im internen Code tatsächlich eine Bedingung
strstr(kernel_name, "cutlass") existiert
Performance-Benchmarks
- In den Benchmark-Daten vor und nach der Refaktorisierung wurde ein relativer Performance-Rückgang bei D=64 gegenüber der Variante vor Persistent Attention beobachtet
- Dies wird auf ein Problem beim Instruction Scheduling im Softmax-Teil zurückgeführt
- In Kombination aus fp8-Typ und "cutlass"-Benennung wird jedoch bei bestimmten Contexts und Größen ein deutlich höherer Durchsatz erreicht
- Zusammenfassung repräsentativer Ergebnisse:
- Attention Z=4, H=32, D=64, causal=False:
- vor Persistent: triton-fp16 ca. 383, triton-fp8 ca. 413, cudnn-fp16 ca. 565
- nach Persistent: triton-fp16 ca. 360, triton-fp8 ca. 370
- Attention Z=4, H=32, D=128, causal=True:
- vor Persistent: triton-fp16 ca. 312, triton-fp8 ca. 345, cudnn-fp16 ca. 553
- nach Persistent: triton-fp16 ca. 356, triton-fp8 ca. 351
Entdeckung und Analyse des Naming-Tricks
- Wenn die Zeichenfolge
"cutlass" im Kernel-Namen enthalten ist, wird laut Community-Analyse in NVIDIA ptxas eine experimentelle Optimierungsroutine aktiviert
- In ptxas gibt es eine hartkodierte Logik für Namensabgleich (
strstr(kernel_name, "cutlass"))
- Dieser Trick gilt als aggressive, experimentelle Optimierung; ob sich die numerische Korrektheit verändert, kann je nach Hardware und Situation unterschiedlich sein
Diskussion in der Community
- Mehrere Reviewer stellten Fragen und Analysen zu Performance-Vergleichen, Genauigkeit und der Art der Optimierung an
- Diskutiert wurden auch Inhalte aus dem technischen Bericht von Deepseek sowie Unterschiede auf bestimmten Architekturen wie Hopper-GPUs
- Außerdem wurde thematisiert, wie breit diese Art von Optimierung über Namensänderungen greift und welche Stabilitätsprobleme möglich sind
Fazit und Implikationen
- Dass allein der Name eines Kernels auf Hardware-Ebene spürbare Performance-Unterschiede auslösen kann, ist ein sehr ungewöhnliches Phänomen
- Dass es im NVIDIA-Software-Stack (
pxtas) versteckte Optimierungs-Trigger gibt, deutet darauf hin, dass bei der Entwicklung von AI-Frameworks auch Benennungsregeln die Performance beeinflussen können
- Praktisch sollte man beim Einsatz dieses Tricks Reproduzierbarkeit und Stabilität unbedingt testen und die Optimierungsrichtlinien des Hardware-Anbieters genau beobachten
1 Kommentare
Hacker-News-Kommentar
Wenn man
ptxaszerlegt, sieht man, dass die Logik zum Erkennen des Kernelnamens „cutlass“ hartkodiert ist.Ich vermute, dass NVIDIA hier eine instabile, experimentelle und aggressive Optimierung angewendet hat; wenn man diese Optimierung immer und bedingungslos einschaltet, könnten subtile Bugs entstehen.
GPU-Compiler sind extrem schwierig, und sobald man über grundlegende Optimierungen hinausgehen will, bekommt man fast immer gemischte Ergebnisse zusammen mit Problemen.
Manche Kernel werden schneller, manche langsamer, und es ist sehr schwer, insgesamt eine Leistungssteigerung zu erreichen.
Es kommt fast nie vor, dass genau eine einzelne Optimierung über alle Tests hinweg immer für mehr Geschwindigkeit sorgt.
Meine Erfahrung betrifft zwar GPU-Systeme außerhalb von Nvidia, aber die Situation kommt mir sehr bekannt vor.
Offenbar hat Nvidia ebenfalls Optimierungen gefunden, die für bestimmte Kernel-Sets erstaunliche Ergebnisse liefern und für andere miserable, ohne dabei verlässliche Kriterien gefunden zu haben, um sie automatisch anzuwenden.
Danke für diese Einordnung.
Ich bin nicht aus diesem Bereich (und habe auch zum ersten Mal von diesem Projekt gehört), deshalb konnte ich weder den Titel noch den PR-Inhalt wirklich verstehen.
Das erinnert mich daran, wie ATI (AMD) vor 25 Jahren beim Quake-III-Benchmark beim Schummeln erwischt wurde, als sich die Performance änderte, wenn man die ausführbare Datei in „quack“ umbenannte.
Dazu auch ein paar Links: techreport.com-Review, hardocp.com-Review, 3dcenter.de-Artikel
Falls es jemand wie mir zuerst falsch verstanden hat, hier die Zusammenfassung.
ATI erkannte den Dateinamen „quake“ und veränderte Dinge wie die Texturqualität, um den Benchmark-Score zu erhöhen.
Als Leute die ausführbare Datei in „quack“ umbenannten, stellte sich heraus, dass die Bildqualität stieg und der Score sank; der ATI-Treiber hatte also absichtlich die Qualität reduziert, um mehr Geschwindigkeit zu erzielen.
ATI selbst hat den Dateinamen also nicht geändert, sondern die Nutzer.
Oder die Sache mit dem Intel C++ Compiler, der auf den String „GenuineIntel“ in der Ausgabe geprüft hat.
Der passende Wiki-Link ist hier.
Bis heute machen das alle Vendoren.
Treiber greifen in die Rendering-Loops populärer Spiele ein, beheben Bugs, ersetzen Shader durch stärker optimierte Versionen oder öffnen schnelle Codepfade.
Solche Änderungen sollten das Ergebnis nur minimal beeinflussen, aber in manchen Fällen wird so aggressiv eingegriffen, dass die Qualität massiv leidet.
So sorgen sie dafür, dass Spiele auf ihrer eigenen Hardware schneller laufen.
Es gibt auch einen Thread mit einer tiefergehenden Diskussion zu diesem Thema, siehe hier.
Interessanterweise ist das ein älterer Beitrag genau zu derselben cutlass-Optimierung.
Solche Fälle sind sehr häufig.
Handy-Chipsatz-Hersteller haben bei Benchmarks getrickst (VW bei Abgaswerten, Nvidia bei 3DMark-Benchmarks, Intel bei SPEC-Benchmarks für Xeon usw.).
In der Computergrafik ist es inzwischen fast schon Usus, dass Treiber pro Spiel eigene Tweaks, Einstellungen und Workarounds einbauen.
(Leider verschwinden heute richtige Quellen viel zu oft, sodass man auf archive.org zurückgreifen muss. Solche Erinnerungen sollte man bewahren.)
Referenzen: Mediatek-Benchmark-Manipulation, Volkswagen-Abgasskandal, Nvidia-3DMark-Kontroverse, Einfluss des Intel-Compilers auf SPEC-Benchmarks
Als jemand, der mit Compilern arbeitet, kann ich sagen, dass Optimierungen dieser Art manchmal tatsächlich von Namen abhängen (Schema, Teilstring usw.).
Es gefällt einem vielleicht nicht, aber in der Praxis funktioniert es eben so.
Das ist nicht unbedingt böswillig; es kann auch die sicherere Wahl sein, eine Optimierung nur auf die eigene Bibliothek anzuwenden, statt das Risiko einzugehen, alles kaputtzumachen.
Oder es liegt daran, dass das Frontend keine verlässlicheren Informationen liefern kann, etwa strukturelle Informationen.
Ich halte so einen Ansatz nicht für hilfreich.
Ich finde das nicht besonders neu.
Das erinnert mich an einen bestimmten Webpack-Stand von vor etwa 10 Jahren, bei dem ein Dateiname wie
add.svgden Build kaputtmachen konnte.Am Ende haben wir es gelöst, indem wir die Datei in
plus.svgumbenannt haben.Es gibt Leute, die numpy unabsichtlich kaputtgemacht haben, indem sie eine Datei namens
secret.pyangelegt haben.Beeindruckend, wie ehrlich die Commit-Message formuliert ist.
Es gab Kritik daran, wie manche ihre Commit-Diffs strukturieren.
Jemand schreibt „ich habe etwas um ungefähr 100 tflops schneller gemacht“, und dann kommt als Reaktion ausgerechnet „die Commit-Message ist schlecht“ … dann würde diese Person vermutlich auch John Carmacks Commit-Stil nicht mögen.
Ich persönlich mag solche ehrlichen Nachrichten.
Das ist mir viel lieber als lauter AI-generierte Commit-Messages im Stil von „refactored X“, die alle gleich klingen.
Ich finde, man sollte squashen und die Commit-Historie zusammenfassen.
Ich verstehe nicht so recht, warum das beim Hochladen auf GitHub nicht gesquasht wurde.
Das erinnert mich daran, wie ich den Umgang mit NVIDIA Jetson gelernt habe.
Damals habe ich erfahren, dass ein einziger Befehl alles schneller machen kann (relevanter Link).
Laut Artikel gibt es 5 W und 10 W, und 10 W ist Standard; man könnte das also eher so lesen, dass es nur dann „schneller“ wird, wenn man vom Standard auf 5 W umstellt. Vielleicht habe ich aber auch etwas missverstanden.
Wenn man stark getunten Code in einer Hochsprache wie C++ schreibt und gleichzeitig konkrete Resultate auf Ebene der GPU-Assemblersprache erwartet, kommt es vor, dass der Compiler nicht das erzeugt, was man will.
Das Compiler-Team kann einem dann verschiedene Lösungen anbieten, aber bei Open-Source-Code lassen sich viele davon nicht anwenden (zum Beispiel proprietäre
#pragmas, Intrinsics usw.).Wenn man Hochleistungsbibliotheken entwickelt, kann man ein Produkt oft gar nicht ausliefern, wenn die Leistung nicht stimmt.
In solchen Fällen bleibt einem manchmal nichts anderes übrig, als über Funktionsnamen oder Ähnliches bestimmte Code-Transformationen auszulösen.
Solche Optimierungen sind in der realen Welt weit verbreitet und lassen sich meiner Meinung nach nicht mit Fällen gleichsetzen, in denen man zum Schummeln bei Benchmarks die Bildqualität absichtlich senkt.
Das war schon Thema, als dieser PR ursprünglich veröffentlicht wurde.
Für mich ist daran nichts neu.
Frühere Diskussion
Ich wünschte, es gäbe eine Wirtschaftsstruktur, in der sich Code leicht teilen ließe.
Nicht dieses komplizierte Konstrukt aus Binary-Blobs, Treibern, Basebands und Ähnlichem, sondern eine Welt, in der alle leicht an Informationen kommen und so Zeit- und Arbeitsverschwendung verringert wird.
Unzählige brillante Ingenieure und Forscher verbringen Monate oder Jahre damit, Dokumentation, Schaltpläne und Binärcode zu reverse-engineeren und zu entschlüsseln, die eigentlich schon jemand besitzt.
CUDA und Firmen wie NVIDIA wirken auf mich frustrierend.
Bei Desktop-Apps liegen die zusätzlichen Kosten pro weiterem Nutzer praktisch bei null.
Deshalb braucht Software ein Modell mit fortlaufenden regelmäßigen Einnahmen, aber die Einnahmen sollten nicht einfach proportional zur Zahl der Nutzer weiter ansteigen.
Diejenigen, die in neue Codeentwicklung investiert haben, müssen ihre Investition zurückbekommen können, wenn das Produkt populär wird, aber klassisches Crowdfunding erfüllt diese Struktur nicht.
Je mehr Nutzer dazukommen, desto stärker sinkt der Beitrag pro Kopf; selbst wenn alle nur 100 %, 10 % oder sogar 1 % beitragen, kann die Gesamtsumme astronomisch werden.
Deshalb halte ich ein pledge-basiertes Auktionsmodell für nötig.
Auch dieses Modell hat Probleme, insbesondere der Übergang zwischen exklusiven und nicht exklusiven Modi ist schwierig.
Wenn einige große Unternehmen die gesamten Entwicklungskosten tragen und der Code dann als Open Source veröffentlicht wird, sind alle übrigen Privatpersonen und Unternehmen faktisch Trittbrettfahrer.