Was macht gccs Fast-Mathe eigentlich?

144

Ich verstehe gcc's --ffast-math Flag von Geschwindigkeit für Float-Ops erheblich erhöhen kann und außerhalb der IEEE-Standards liegt, aber ich kann anscheinend keine Informationen darüber finden, was wirklich passiert, wenn es eingeschaltet ist. Kann jemand bitte einige Details erklären und vielleicht ein klares Beispiel dafür geben, wie sich etwas ändern würde, wenn die Flagge ein- oder ausgeschaltet wäre?

Ich habe versucht, SO nach ähnlichen Fragen zu durchsuchen, konnte aber nichts finden, was die Funktionsweise von ffast-math erklärt.

Ponml
quelle

Antworten:

86

Wie Sie bereits erwähnt haben, sind Optimierungen möglich, bei denen die strikte IEEE-Konformität nicht eingehalten wird.

Ein Beispiel ist folgendes:

x = x*x*x*x*x*x*x*x;

zu

x *= x;
x *= x;
x *= x;

Da Gleitkomma-Arithmetik nicht assoziativ ist, wirkt sich die Reihenfolge und das Factoring der Operationen aufgrund von Rundungen auf die Ergebnisse aus. Daher wird diese Optimierung nicht unter striktem FP-Verhalten durchgeführt.

Ich habe nicht überprüft, ob GCC diese spezielle Optimierung tatsächlich durchführt. Aber die Idee ist dieselbe.

Mystisch
quelle
25
@ Andrerey: Für dieses Beispiel gehen Sie von 7 Multiplikationen auf 3.
Mysticial
4
@Andrey: Mathematisch wird es richtig sein. Das Ergebnis kann sich jedoch aufgrund der unterschiedlichen Rundung in den letzten Bits geringfügig unterscheiden.
Mysticial
1
In den meisten Fällen spielt dieser geringfügige Unterschied keine Rolle (relativ in der Größenordnung von 10 ^ -16 für double, variiert jedoch je nach Anwendung). Eine Sache, die zu beachten ist, ist, dass ffast-math-Optimierungen nicht unbedingt "mehr" Abrundungen hinzufügen. Der einzige Grund, warum es nicht IEEE-konform ist, ist, dass die Antwort (wenn auch geringfügig) von der geschriebenen abweicht.
Mysticial
1
@user: Die Größe des Fehlers hängt von den Eingabedaten ab. Sie sollte im Verhältnis zum Ergebnis klein sein. Wenn zum Beispiel xkleiner als 10 ist, ist der Fehler in Mysticals Beispiel um 10 ^ -10 gesunken. Aber wenn ja x = 10e20, wird der Fehler wahrscheinlich viele Millionen betragen.
Ben Voigt
3
@stefanct ist es eigentlich zu -fassociative-mathdem in inbegriffen -funsafe-math-optimizationsdenen wiederum sind mit -ffast-math Warum nicht GCC optimize a*a*a*a*a*azu (a*a*a)*(a*a*a)?
Phuclv
255

-ffast-math macht viel mehr als nur die strikte IEEE-Konformität zu brechen.

Zuallererst verstößt es natürlich gegen die strikte IEEE-Konformität, was beispielsweise die Neuordnung von Anweisungen zu etwas ermöglicht, das mathematisch (idealerweise) aber im Gleitkomma nicht genau gleich ist.

Zweitens wird die Einstellung errnonach mathematischen Funktionen mit einem Befehl deaktiviert , was bedeutet, dass ein Schreiben in eine threadlokale Variable vermieden wird (dies kann bei einigen Architekturen einen 100% igen Unterschied für diese Funktionen bewirken).

Drittens wird davon ausgegangen, dass die gesamte Mathematik endlich ist , was bedeutet, dass keine Überprüfungen auf NaN (oder Null) durchgeführt werden, an denen sie nachteilige Auswirkungen haben würden. Es wird einfach angenommen, dass dies nicht passieren wird.

Viertens ermöglicht es reziproke Approximationen für Division und reziproke Quadratwurzel.

Außerdem wird die vorzeichenbehaftete Null deaktiviert (Code geht davon aus, dass die vorzeichenbehaftete Null nicht vorhanden ist, auch wenn das Ziel dies unterstützt) und die Rundungsmathematik, die unter anderem eine konstante Faltung zur Kompilierungszeit ermöglicht.

Last, es Code generiert, aufgrund Signalisierung / Trapping math passieren kann keine Hardware - Interrupts geht davon aus, dass (das heißt, wenn diese nicht deaktiviert auf der Zielarchitektur sein kann und folglich passieren kann , werden sie nicht behandelt werden).

Damon
quelle
15
Damon, danke! Können Sie einige Referenzen hinzufügen? Wie gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html " -ffast-math Setzt -fno-math-errno, -funsafe-math-Optimierungen, -ffinite-math-only, -fno-rounding-math, -fno-Signaling -nans und -fcx-Limited-Range. Diese Option bewirkt, dass das Präprozessor-Makro FAST_MATH definiert wird. "und etwas von glibc, wie (in der math.hNähe von math_errhandling)" Standardmäßig unterstützen alle Funktionen sowohl die Fehler- als auch die Ausnahmebehandlung. Im schnellen Mathematikmodus von und Wenn Inline-Funktionen definiert sind, ist dies möglicherweise nicht der Fall. "
osgx
4
@javapowered: Ob es "gefährlich" ist, hängt davon ab, welche Garantien Sie benötigen. -ffast-mathermöglicht es dem Compiler, einige Ecken zu kürzen und einige Versprechen zu brechen (wie erklärt), was im Allgemeinen als solches nicht gefährlich und für die meisten Menschen kein Problem darstellt. Für die meisten Menschen ist es das gleiche, nur schneller. Wenn Ihr Code diese Versprechen jedoch annimmt und sich darauf verlässt , verhält sich Ihr Code möglicherweise anders als erwartet. Normalerweise bedeutet dies , dass das Programm scheint zu funktionieren, vor allem, aber einige Ergebnisse können „unerwartete“ (sagen wir, in einer Physik - Simulation, zwei Objekte möglicherweise nicht kollidieren richtig).
Damon
2
@ Royi: Die beiden sollten unabhängig voneinander sein. -O2Aktiviert im Allgemeinen "jede" rechtliche Optimierung, mit Ausnahme derjenigen, die Größe gegen Geschwindigkeit tauschen. -O3ermöglicht auch Optimierungen, bei denen Größe gegen Geschwindigkeit getauscht wird. Es behält immer noch 100% Korrektheit bei. -ffast-mathVersuche, mathematische Operationen schneller zu machen, indem "leicht falsches" Verhalten zugelassen wird, das normalerweise nicht schädlich ist, aber vom Wortlaut der Norm als falsch angesehen wird. Wenn Ihr Code auf zwei Compilern tatsächlich sehr unterschiedlich schnell ist (nicht nur 1-2%), überprüfen Sie, ob Ihr Code streng standardkonform ist und ...
Damon
1
... erzeugt keine Warnungen. Stellen Sie außerdem sicher, dass Sie Aliasing-Regeln und Dingen wie der automatischen Vektorisierung nicht im Wege stehen. Grundsätzlich sollte GCC mindestens so gut (meiner Erfahrung nach normalerweise besser) sein wie MSVC. Wenn dies nicht der Fall ist, haben Sie wahrscheinlich einen subtilen Fehler gemacht, den MSVC einfach ignoriert, der jedoch dazu führt, dass GCC eine Optimierung deaktiviert. Sie sollten beide Optionen angeben, wenn Sie beide möchten, ja.
Damon
1
@ Royi: Dieser Code sieht für mich nicht wirklich klein und einfach aus, nicht etwas, das man in wenigen Minuten (oder sogar Stunden) gründlich analysieren könnte. Unter anderem handelt es sich um eine scheinbar harmlose #pragma omp parallel forund innerhalb des Schleifenkörpers lesen und schreiben Sie an Adressen, auf die durch Funktionsargumente verwiesen wird, und führen eine nicht triviale Verzweigung durch. Als ungebildete Vermutung könnten Sie Caches aus Ihrem implementierungsdefinierten Aufruf von Threads heraus verprügeln, und MSVC vermeidet möglicherweise fälschlicherweise Zwischenspeicher, für die Aliasing-Regeln vorgeschrieben wären. Unmöglich zu sagen.
Damon