[C++] Mikrooptimierungs-Log

Hier können Artikel, Tutorials, Bücherrezensionen, Dokumente aller Art, Texturen, Sprites, Sounds, Musik und Modelle zur Verfügung gestellt bzw. verlinkt werden.
Forumsregeln
Möglichst sinnvolle Präfixe oder die Themensymbole nutzen.

Re: [C++] Mikrooptimierungs-Log

Beitragvon Spiele Programmierer » 02.03.2015, 19:00

Interessant.
Also Clang setzt die Minimum und Maximum Befehle glücklicherweise schon automatisch ein.
Ich finde, Microsofts Compiler sollte das auch können. In vektorisierten Code kann es auch Visual Studio. Also warum nicht auch sonst? Ist mir völlig unverständlich.

Bezüglich Quadratwurzel scheint übrigens das Microsoftteam zufällig eine andere Ansicht zu vertreten:
https://connect.microsoft.com/VisualStudio/Feedback/Details/880213
Spiele Programmierer
 
Beiträge: 329
Registriert: 23.01.2013, 16:55

Re: [C++] Mikrooptimierungs-Log

Beitragvon Krishty » 02.03.2015, 19:28

Ja, klingt tatsächlich nach einer Datenabhängigkeit, die bei der vektorisierten Variante entfällt. Ulkigerweise erwartet _mm_sqrt_sd() ein zusätzliches Register zum Rumkritzeln (hat zwei Parameter statt einem) – möglicherweise nullen die das um die Datenabhängigkeit zu vermeiden, und haben dafür ein Register verschwendet. Da kann man mit der vektorisierten Variante echt nur gewinnen.

Ich hätte das benchen sollen, statt nur die Timings nachzusehen :) Das Compiler-Team sagt, dass die double-Varianten skalar und vektorisiert die gleiche Ausführungszeit hätten – ich habe nur für die float-Varianten ins Handbuch gesehen, wo die vektorisierte Variante leicht höhere Latenz hatte.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
 
Beiträge: 5601
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: [C++] Mikrooptimierungs-Log

Beitragvon Krishty » 28.06.2016, 21:21

Mal was für Optimierung auf Größe, das Visual C++ 2015 verpennt:

  if(x >= 128)

produziert eine Folge von Vergleich und Jump-if-above-or-equal:

48 3D 80 00 00 00    cmp    rax,00000080h
73 13                jae    foo+46h


Weil 128 nicht in ein 1-B-signed char passt, den cmp als Operand nutzen kann, wird die Variante mit int als Operand gewählt. Kompakter ist

  if(x > 127)

mit den resultierenden Befehlen

48 83 F8 7F          cmp    rax,7Fh
77 13                ja     foo+44h


Zwei Bytes gespart. Bedenkt, dass das auch für x < 128 gilt (besser x <= 127)!
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
 
Beiträge: 5601
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Re: [C++] Mikrooptimierungs-Log

Beitragvon Krishty » 26.03.2017, 17:39

*seufz* Machen wir mal String-zu-Integer in Visual C++ schneller …

… das betrifft nämlich so ziemlich alle textbasierten Formate.

Hinweis: Nutzt niemals ein textbasiertes Format für irgendwas Performance-kritisches!

Also … meine String-zu-Integer-Routine hat im Kern so eine Schleife:

  while(toChar < toEnd && isDecimalDigit(*toChar)) {
    result = 10 * result + numberFromDecimalDigit(*toChar);
    ++toChar;
  }


[Es gibt andere Schleifenarten – aber hier geht es erstmal nur um die Mikrooptimierung!]

Wir prüfen also erstmal, ob wir das Ende des Strings erreicht haben. Dann, ob eine Ziffer zwischen 0 und 9 folgt. Falls ja, verzehnfachen wir die bisherige Zahl und addieren die neue Ziffer auf.

  bool isDecimalDigit(char c) {
    // c >= '0' && c <= '9'
würde auch gehen, aber SUB+Sprung ist schneller als zwei Sprünge
    // durch den Cast zu unsigned werden alle Buchstaben, die in ASCII vor der Ziffer '0' kommen, zu sehr großen Zahlen
    return unsigned(c - '0') < 9;
  }

  int numberFromDecimalDigit(char c) {
    return c - '0';
  }


Für die Folge „123“ sind das also drei Durchläufe:
  1. result == 0; 10 * 0 + 1 == 1;
  2. result == 1; 10 * 1 + 2 == 12;
  3. result == 2; 10 * 12 + 3 == 123;

Wir schauen ins Disassembly, und … eeeeeeeeew:

  movsx eax,byte ptr [rax]
  sub eax,30h
  cmp al,9
  …
  movsx eax,byte ptr [rax]
  sub eax,30h


Wir laden zwei Mal aus *toChar, und Visual C++ hat daraus tatsächlich zwei Loads und zwei Subtraktionen gemacht!

Hinweis: Clang und GCC könnten hier bessere Befehle produzieren. Ich nehme Tests dankend entgegen!

Also von Hand auflösen:

  while(toChar < toEnd) {
    auto digit = numberFromDecimalDigit(*toChar);
    if(9 < digit) {
      break; // keine Ziffer
    }
    result = 10 * result + digit;
    ++toChar;
  }


Ergebnis: String-zu-float ist 25 % schneller; String-zu-int 20 %. (Integer haben meist weniger Ziffern als Gleitkommazahlen, da fällt die Verbesserung weniger stark ins Gewicht.) Textbasiertes 3D-Dateiformat ist insgesamt 15 % schneller. Scheiß Compiler.
seziert Ace Combat, Driver, und S.T.A.L.K.E.R.   —   rendert Sterne
Benutzeravatar
Krishty
 
Beiträge: 5601
Registriert: 26.02.2009, 12:18
Benutzertext: state is the enemy

Vorherige

Zurück zu Artikel, Tutorials und Materialien

Wer ist online?

Mitglieder in diesem Forum: 0 Mitglieder und 1 Gast