Que fait réellement le ffast-math de gcc?

144

Je comprends que le --ffast-mathdrapeau de gcc peut considérablement augmenter la vitesse des opérations flottantes et va en dehors des normes IEEE, mais je n'arrive pas à trouver des informations sur ce qui se passe réellement quand il est activé. Quelqu'un peut-il expliquer certains détails et peut-être donner un exemple clair de la façon dont quelque chose changerait si le drapeau était activé ou désactivé?

J'ai essayé de fouiller dans SO pour des questions similaires, mais je n'ai rien trouvé expliquant le fonctionnement de ffast-math.

Ponml
la source

Réponses:

86

Comme vous l'avez mentionné, il permet des optimisations qui ne préservent pas la stricte conformité IEEE.

Un exemple est celui-ci:

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

à

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

L'arithmétique à virgule flottante n'étant pas associative, l'ordre et la factorisation des opérations affecteront les résultats en raison de l'arrondi. Par conséquent, cette optimisation n'est pas effectuée sous un comportement FP strict.

Je n'ai pas vraiment vérifié pour voir si GCC fait réellement cette optimisation particulière. Mais l'idée est la même.

Mysticial
la source
25
@Andrey: Pour cet exemple, vous passez de 7 multiplications à 3.
Mysticial
4
@Andrey: Mathématiquement, ce sera correct. Mais le résultat peut différer légèrement dans les derniers bits en raison des différents arrondis.
Mysticial le
1
Dans la plupart des cas, cette légère différence n'aura pas d'importance (relativement de l'ordre de 10 ^ -16 pour double, mais varie en fonction de l'application). Une chose à noter est que les optimisations ffast-math n'apportent pas nécessairement «plus» d'arrondi. La seule raison pour laquelle ce n'est pas conforme IEEE est que la réponse est différente (quoique légèrement) de ce qui est écrit.
Mysticial le
1
@user: L'ampleur de l'erreur dépend des données d'entrée. Il doit être petit par rapport au résultat. Par exemple, si xest inférieur à 10, l'erreur dans l'exemple de Mystical sera vers le bas autour de 10 ^ -10. Mais si x = 10e20, l'erreur est susceptible d'être de plusieurs millions.
Ben Voigt le
3
@stefanct il est en fait de ce -fassociative-mathqui est inclus dans ce -funsafe-math-optimizationsqui est à son tour activé avec -ffast-math Pourquoi permet pas d' optimiser GCC a*a*a*a*a*aà (a*a*a)*(a*a*a)?
phuclv
256

-ffast-math fait bien plus que casser la stricte conformité IEEE.

Tout d'abord, bien sûr, il ne respecte pas la stricte conformité IEEE, permettant par exemple de réorganiser les instructions en quelque chose qui est mathématiquement le même (idéalement) mais pas exactement le même en virgule flottante.

Deuxièmement, il désactive le réglage errnoaprès les fonctions mathématiques à instruction unique, ce qui signifie éviter d'écrire dans une variable locale de thread (cela peut faire une différence de 100% pour ces fonctions sur certaines architectures).

Troisièmement, il suppose que toutes les mathématiques sont finies , ce qui signifie qu'aucune vérification de NaN (ou zéro) n'est effectuée là où elles auraient des effets néfastes. On suppose simplement que cela n'arrivera pas.

Quatrièmement, il permet des approximations réciproques pour la division et la racine carrée réciproque.

De plus, il désactive le zéro signé (le code suppose que le zéro signé n'existe pas, même si la cible le prend en charge) et l'arrondi mathématique, ce qui permet entre autres un repliement constant au moment de la compilation.

Enfin, il génère du code qui suppose qu'aucune interruption matérielle ne peut se produire en raison de la signalisation / du piégeage mathématique (c'est-à-dire que si celles-ci ne peuvent pas être désactivées sur l'architecture cible et se produisent par conséquent , elles ne seront pas traitées).

Damon
la source
15
Damon, merci! Pouvez-vous ajouter quelques références? Comme gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html " -ffast-math Définit -fno-math-errno, -funsafe-math-optimisations, -ffinite-math-only, -fno-rounding-math, -fno-signaling -nans et -fcx-limited-range. Cette option entraîne la définition de la macro de préprocesseur FAST_MATH . "et quelque chose de la glibc, comme ( math.hnear math_errhandling)" Par défaut, toutes les fonctions prennent en charge à la fois errno et la gestion des exceptions. En mode mathématique rapide de gcc et si les fonctions en ligne sont définies, cela peut ne pas être vrai. "
osgx
4
@javapowered: Que ce soit "dangereux" dépend des garanties dont vous avez besoin. -ffast-mathpermet au compilateur de couper quelques angles et de rompre certaines promesses (comme expliqué), ce qui en général n'est pas dangereux en tant que tel et ne pose aucun problème pour la plupart des gens. Pour la plupart des gens, c'est la même chose, mais plus rapidement. Cependant, si votre code suppose et s'appuie sur ces promesses, votre code peut se comporter différemment de ce à quoi vous vous attendez. Habituellement, cela signifie que le programme semble fonctionner correctement, la plupart du temps, mais certains résultats peuvent être "inattendus" (par exemple, dans une simulation physique, deux objets peuvent ne pas entrer en collision correctement).
Damon
2
@Royi: Les deux devraient être indépendants l'un de l'autre. -O2permet généralement "toutes" les optimisations légales, sauf celles qui tradent la taille pour la vitesse. -O3permet également des optimisations qui troquent la taille pour la vitesse. Il maintient toujours 100% d'exactitude. -ffast-mathtente d'accélérer les opérations mathématiques en autorisant un comportement «légèrement incorrect» qui n'est généralement pas nuisible, mais qui serait considéré comme incorrect par le libellé de la norme. Si votre code est en effet très différent en vitesse sur deux compilateurs (pas seulement 1 à 2%) alors vérifiez que votre code est strictement conforme aux normes et ...
Damon
1
... ne produit aucun avertissement. Assurez-vous également de ne pas gêner les règles d'aliasing et des choses comme la vectorisation automatique. En principe, GCC devrait fonctionner au moins aussi bien (généralement mieux d'après mon expérience) que MSVC. Lorsque ce n'est pas le cas, vous avez probablement commis une erreur subtile que MSVC ignore tout simplement mais qui oblige GCC à désactiver une optimisation. Vous devriez donner les deux options si vous voulez les deux, oui.
Damon
1
@Royi: Ce code ne me semble pas vraiment petit et simple, pas quelque chose que l'on pourrait analyser en profondeur en quelques minutes (ou même des heures). Entre autres choses, cela implique un élément apparemment inoffensif #pragma omp parallel for, et dans le corps de la boucle, vous lisez et écrivez à la fois des adresses pointées par des arguments de fonction et effectuez une quantité non négligeable de branchements. En guise de supposition sans instruction, vous pourriez éliminer les caches à partir de votre appel de threads défini par l'implémentation, et MSVC peut éviter à tort les magasins intermédiaires que les règles d'alias imposent. Impossible de dire.
Damon