J'ai entendu de diverses sources (bien que principalement par un de mes collègues) que compiler avec un niveau d'optimisation de -O3
en g ++ est en quelque sorte "dangereux" et devrait être évité en général, sauf preuve du contraire.
Est-ce vrai et si oui, pourquoi? Devrais-je simplement m'en tenir à -O2
?
Dans les premiers jours de gcc (2,8 etc.) et à l'époque d'egcs, redhat 2,96 -03 était parfois assez bogué. Mais cela remonte à plus de dix ans et -O3 n’est pas très différent des autres niveaux d’optimisation (dans buggyness).
Il a toutefois tendance à révéler des cas dans lesquels des personnes s’appuient sur un comportement indéfini, car elles s’appuient plus strictement sur les règles, et plus particulièrement sur les cas délicats, de la langue.
En guise de note personnelle, j'utilise un logiciel de production dans le secteur financier depuis plusieurs années avec -O3 et je n'ai pas encore rencontré de bogue qui n'aurait pas existé si j'avais utilisé -O2.
A la demande générale, voici un ajout:
L'option -O3 et en particulier les indicateurs supplémentaires tels que -funroll-loops (non activé par -O3) peuvent parfois entraîner la génération de plus de code machine. Dans certaines circonstances (par exemple sur une unité centrale avec une mémoire cache d'instructions L1 exceptionnellement petite), cela peut entraîner un ralentissement du fait de la totalité du code, par exemple. une boucle interne ne correspond plus à L1I. Généralement, gcc s'efforce de ne pas générer autant de code, mais comme cela optimise généralement le cas générique, cela peut arriver. Les options particulièrement sujettes à cela (comme le déroulement d'une boucle) ne sont normalement pas incluses dans -O3 et sont marquées en conséquence dans la page de manuel. En tant que tel, il est généralement judicieux d’utiliser -O3 pour générer du code rapide et de ne retomber que sur -O2 ou -Os (qui tente d’optimiser la taille du code) le cas échéant (par exemple, lorsqu'un profileur indique des ratés L1I).
Si vous voulez prendre l'optimisation à l'extrême, vous pouvez modifier dans gcc via --param les coûts associés à certaines optimisations. De plus, notez que gcc a maintenant la possibilité de placer des attributs dans des fonctions qui contrôlent les paramètres d’optimisation uniquement pour ces fonctions. Ainsi, lorsque vous constatez que vous rencontrez un problème avec -O3 dans une fonction (ou que vous souhaitez essayer des indicateurs spéciaux pour cette fonction uniquement), vous n'avez pas besoin de compiler tout le fichier ou même tout le projet avec O2.
otoh, il semble qu’il faille prendre des précautions lors de l’utilisation de -Ofast, qui indique:
-Ofast active toutes les optimisations -O3. Il permet également des optimisations non valables pour tous les programmes conformes aux normes.
ce qui me fait conclure que -O3 est destiné à être entièrement conforme aux normes.
Cela est déjà dit dans la réponse de Neel, mais pas clairement ni assez fort:
Dans mon expérience un peu en damiers, appliquer -O3
à un programme entier presque toujours le ralentit (par rapport à -O2
), car il active un déroulement et un alignement en boucle agressifs qui rendent programme ne rentre plus dans le cache des instructions. Pour les programmes plus importants, cela peut également être vrai pour -O2
par rapport à -Os
!
Le modèle d'utilisation prévu pour -O3
est, après avoir profilé votre programme, que vous l'appliquiez manuellement à une petite poignée de fichiers contenant des boucles internes critiques qui bénéficient réellement de ces compromis agressifs d'espace-pour-vitesse. Avec le GCC très récent, je pense que le nouveau mode d'optimisation guidé par profil de lien-temps peut appliquer de manière sélective les optimisations -O3
aux fonctions dynamiques - - automatiser efficacement ce processus.
L’option -O3 active des optimisations plus coûteuses, telles que l’intégration de fonctions, en plus de toutes les optimisations des niveaux inférieurs "-O2" et "-O1". Le niveau d’optimisation "-O3" peut augmenter la vitesse de l’exécutable résultant, mais peut également augmenter sa taille. Dans certaines circonstances où ces optimisations ne sont pas favorables, cette option peut en réalité ralentir un programme.
Oui, O3 est buggier. Je suis un développeur de compilateur et j'ai identifié des bogues gcc clairs et évidents causés par la génération par O3, dans le cas contraire, d'instructions d'assemblage SIMD lors de la création de mon propre logiciel. D'après ce que j'ai vu, la plupart des logiciels de production sont livrés avec O2, ce qui signifie que O3 aura moins d'attention pour les tests et les corrections de bugs.
Pensez-y de cette façon: O3 ajoute plus de transformations sur O2, ce qui ajoute plus de transformations sur O1. Statistiquement, plus de transformations signifie plus de bugs. C'est vrai pour tout compilateur.
Récemment, j'ai rencontré un problème d'utilisation de l'optimisation avec g++
. Le problème était lié à une carte PCI, où les registres (pour la commande et les données) étaient représentés par une adresse mémoire. Mon pilote a mappé l'adresse physique sur un pointeur de l'application et l'a transmise au processus appelé, qui a fonctionné comme suit:
unsigned int * pciMemory;
askDriverForMapping( & pciMemory );
...
pciMemory[ 0 ] = someCommandIdx;
pciMemory[ 0 ] = someCommandLength;
for ( int i = 0; i < sizeof( someCommand ); i++ )
pciMemory[ 0 ] = someCommand[ i ];
La carte n'a pas agi comme prévu. Quand j'ai vu l'Assemblée, j'ai compris que le compilateur n'a écrit que someCommand[ the last ]
en pciMemory
, en omettant toutes les écritures précédentes.
En conclusion: soyez précis et attentif à l'optimisation.