45 votes

Différents niveaux d'optimisation peuvent-ils conduire à un code fonctionnellement différent?

Je suis curieux de connaître les libertés qu'un compilateur a lors de l'optimisation. Limitons à cette question pour GCC et C/C++ (n'importe quelle version, toute la saveur de la norme):

Est-il possible d'écrire du code qui se comporte différemment en fonction du niveau d'optimisation il a été compilé avec?

L'exemple que j'ai en tête est l'impression de différents morceaux de texte dans plusieurs constructeurs en C++ et en faisant une différence selon que les copies sont élidés (même si je n'ai pas pu faire une telle chose travail).

Comptage de cycles d'horloge n'est pas autorisée. Si vous avez un exemple pour un non-compilateur GCC, je serais curieux aussi, mais je ne peux pas le vérifier. Les points de Bonus pour un exemple dans C. :-)

Edit: Le code de l'exemple doit être conforme à la norme et ne pas contenir un comportement indéterminé dès le départ.

Edit 2: Obtenu quelques grandes réponses déjà! Permettez-moi de faire monter les enjeux un peu: Le code doit être bien formé programme et être conformes aux normes en vigueur, et il doit compiler pour corriger, déterministe des programmes dans chaque niveau d'optimisation. (Qui exclut des choses comme la course-conditions de mal-formé code multithread.) Aussi j'apprécie flottantes point d'arrondi peuvent être touchés, mais nous allons rabais qui.

Je viens de toucher 800 réputation, donc je pense que je vais sauter 50 réputation de bounty sur le premier exemple complet pour se conformer à (l'esprit) de ces conditions; 25 si elle implique abuser stricte de l'aliasing. (Sous réserve que quelqu'un me montre comment envoyer des bounty à quelqu'un d'autre.)

19voto

Robᵩ Points 50501

La partie de la norme C++ qui s'applique est §1.9 "l'exécution du Programme". Il se lit en partie:

conforme implémentations sont nécessaires pour émuler (seulement) les comportements observables de la machine abstraite comme expliqué ci-dessous. ...

Conforme de la mise en œuvre de l'exécution d'un bien formé programme, il doit produire les mêmes comportements observables comme l'un des possibles de l'exécution de séquences de l'instance correspondante de la machine abstraite avec le même programme et la même entrée. ...

Les comportements observables de la machine abstraite est sa séquence de lecture et d'écriture à la volatilité des données et des appels à la bibliothèque de fonctions d'e/S. ...

Donc, oui, le code peut se comporter différemment à différents niveaux d'optimisation, mais (en supposant que tous les niveaux produire un compilateur conforme), mais ils ne peuvent pas se comporter observable différemment.

EDIT: Permettez-moi de corriger ma conclusion: Oui, le code peut se comporter différemment à différents niveaux d'optimisation, à condition que chaque comportement est observable identique à l'un des comportements de la norme de la machine abstraite.

16voto

Dennis Zickefoose Points 6659

Les calculs en virgule flottante sont une source mûre de différences. En fonction de la manière dont les opérations individuelles sont ordonnées, vous pouvez obtenir plus / moins d'erreurs d'arrondi.

Des codes multithread peu sûrs peuvent également avoir des résultats différents en fonction de l'optimisation des accès à la mémoire, mais il s'agit quand même d'un bogue dans votre code.

Et comme vous l'avez mentionné, les effets secondaires des constructeurs de copie peuvent disparaître lorsque les niveaux d'optimisation changent.

14voto

BЈовић Points 28674

Est-il possible d'écrire du code qui se comporte différemment en fonction du niveau d'optimisation pour lequel il a été compilé?

Seulement si vous déclenchez un bogue du compilateur.

MODIFIER

Cet exemple se comporte différemment sur gcc 4.5.2:

 void foo(int i) {
  foo(i+1);
}

main() {
  foo(0);
}
 

Compilé avec -O0 crée un programme bloquant avec une erreur de segmentation.
Compilé avec -O2 crée un programme entrant dans une boucle sans fin.

11voto

Steve Jessop Points 166970

OK, mon flagrante jouer pour le bounty, en fournissant un exemple concret. Je vais mettre ensemble les éléments à partir d'autres réponses et mes commentaires.

Dans le but de changer de comportement à différentes optimisations niveaux, d'optimisation au niveau d'Un" désigne gcc -O0 (j'utilise la version 4.3.4, mais il n'a pas beaucoup d'importance, je pense à tout, même vaguement, récente version de montrer la différence que je suis après), et d'optimisation "niveau B" désigne gcc -O0 -fno-elide-constructors.

Le Code est simple:

#include <iostream>

struct Foo {
    ~Foo() { std::cout << "~Foo\n"; }
};

int main() {
    Foo f = Foo();
}

De sortie à Un niveau d'optimisation:

~Foo

Sortie au niveau d'optimisation B:

~Foo
~Foo

Le code est totalement légal, mais le résultat est dépendant de l'implémentation en raison de constructeur de copie élision, et en particulier sensibles à la gcc de l'option d'optimisation qui désactive la copie ctor élision.

Remarque que, de façon générale, "l'optimisation" fait référence aux compilateur transformations qui peuvent modifier le comportement est indéfini, indéterminé ou la mise en œuvre défini, mais pas le comportement est défini par la norme. Donc, un exemple qui répond à vos critères nécessairement est un programme dont la sortie est soit non précisé ou définis par l'implémentation. Dans ce cas, il est non spécifié par la norme si la copie ctors sont gommés, je viens de passer à la chance que GCC fiable élude à peu près à chaque fois autorisé, mais a une option pour les désactiver.

8voto

Jens Gustedt Points 40410

Pour C, presque toutes les opérations sont strictement définies dans la machine abstraite et optimisations ne sont autorisés que si les observables résultat est exactement celui de la machine abstraite. Les Exceptions à cette règle, qui viennent à l'esprit:

  • un comportement indéfini n'a pas à être cohérente entre les différents compilateur des courses ou des exécutions du code défectueux
  • les opérations à virgule flottante peut provoquer différentes arrondissement
  • des arguments pour les appels de fonctions peuvent être évalué dans n'importe quel ordre
  • expressions avec volatilequalifiés type peuvent ou ne peuvent pas être évaluées de la même manière pour leurs effets secondaires
  • identique const qualifié composé de littéraux peut ou peut ne pas être plié dans un emplacement de mémoire statique

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X