1 Punkte von GN⁺ 2025-02-10 | 1 Kommentare | Auf WhatsApp teilen

Einführung

  • Dieser Artikel möchte mit verbreiteten Irrtümern über Verzweigungen auf GPUs aufräumen.
  • Einige Lernwebsites verbreiten falsche Informationen, die hier korrigiert werden sollen.

Problem

  • Es wird ein Codebeispiel gezeigt, das den ternären Operator verwendet, um bedingte Ausführung in GPU-Code zu implementieren.
  • Manche schlagen als „Optimierung“ vor, dies durch arithmetische Operationen zu ersetzen, doch das beruht auf einem Missverständnis.
  • Der ternäre Operator führt eine bedingte Verschiebung aus, die mit einfachen Bitoperationen implementiert wird.
  • Echte Verzweigungen kommen in GPU-Code zwar vor, werden aber nicht für kleine Registerverschiebungen verwendet.

Probleme der falschen Optimierung

  • Die vorgeschlagene Optimierung läuft in der Praxis sogar langsamer als der ursprüngliche Code.
  • Die Funktion step() ist mit dem ternären Operator implementiert und führt dadurch unnötige zusätzliche Multiplikationen und Additionen aus.
  • Im ursprünglichen Code wird der Wert direkt bedingt verschoben.

Analyse des Maschinencodes

  • Am Maschinencode von AMD- und Microsoft-Compilern lässt sich bestätigen, dass die GPU keine Verzweigung ausführt.
  • Stattdessen wird die bedingte Verschiebung mit Vergleichsoperationen und Bitmasken umgesetzt.

Fazit

  • Optimierungsvorschläge mit der Funktion step() sind Fehlinformationen und sollten korrigiert werden.

  • Diese Fehlinformation verbreitet sich seit über 20 Jahren und muss richtiggestellt werden.

  • Inigo Quilez – lernt seit 1994 Computergrafik.

1 Kommentare

 
GN⁺ 2025-02-10
Hacker-News-Kommentare
  • Ich bin ziemlich sicher, dass das Fazit des Artikels stimmt, aber die Argumentation wäre stärker, wenn die Codegenerierung für beide Versionen gezeigt würde, nicht nur für die bessere

    • Es wird zwar der erzeugte Maschinencode gezeigt, um zu belegen, dass die "optimierte" Version tatsächlich deutlich langsamer läuft als die ursprüngliche, aber es wird nicht bewiesen, dass die schlechte Version schlechter ist
  • Es wäre schön, wenn es eine gute Möglichkeit gäbe zu erkennen, in welchen Fällen if tatsächlich einen echten Branch erzwingt

    • Der Grund, warum Leute die teureren mix/lerps verwenden, ist, dass sie zwar etwas Overhead in Kauf nehmen, aber Angst davor haben, einen Branch zu erzeugen
    • Es ist gut, dass v = x > y ? a : b; tatsächlich funktioniert, aber es ist beunruhigend, dass if eine Syntax ist, die manchmal ein Branch ist und manchmal nicht
  • Auch dieser Artikel ist relevant: Falsche Ratschläge zum Schreiben von Branches auf GPUs richtigstellen

    • Früher waren Optimierungen zum Vermeiden von Branches wirksam, heute sollte man das aber nicht mehr tun
    • Da sich Prozessoren und Compiler ändern, ist es besser, mehrere Varianten bereitzustellen und zur Laufzeit die schnellste auszuwählen
  • Ich frage mich, warum der Compiler nicht erkennt, dass die "optimierte" Version äquivalent ist

    • Er sollte step() verstehen und die Fälle step()=0.0 und step()==1.0 getrennt optimieren können
  • Ich bin selbst schon in dieses Problem gelaufen. Claude/ChatGPT schlagen das ebenfalls als Optimierung vor, aber es verschlechtert die Leistung

    • Danke an Inigo
  • Ich frage mich, wie man erkennen kann, ob eine OpenGL-Funktion emuliert wird, statt eine grundlegende GPU-Funktion direkt aufzurufen

  • Beim Schreiben von Code braucht man Erfahrung, um sicher sein zu können, dass es keine bedingten Branches geben wird

    • Es ist schwer zu wissen, wie viele Operationen nach einer Bedingung einen Branch auslösen und welche Operationen der Compiler weglassen kann
    • Ich frage mich, ob man eine Performance-Test-Suite verwenden sollte, um versehentliche Performance-Einbußen zu erkennen
  • Es wird erklärt, wie eine Variante der Funktion mix für Vektoren arbeitet