2010-04-09 4 views
0

Je programme un algorithme qui contient 4 boucles for imbriquées. Le problème est qu'à chaque niveau un pointeur est mis à jour. La boucle la plus interne utilise seulement 1 des pointeurs. L'algorithme fait un compte compliqué. Lorsque j'inclue une instruction de débogage qui consigne la combinaison des index et les résultats du compte, j'obtiens la bonne réponse. Lorsque l'instruction de débogage est omise, le nombre est incorrect. Le programme est compilé avec l'option -O3 sur gcc. Pourquoi cela arriverait-il?Algorithme de rupture d'optimisation C

+9

Y at-il un code pour cela? Ma conjecture est qu'il y a un bug dans votre code que l'optimisation met en évidence de manière différente que sans optimisation. – WhirlWind

+14

Veuillez poster le code. Bien qu'il soit possible qu'un bogue d'optimisation génère un code incorrect, il est également possible qu'une hypothèse incorrecte dans l'implémentation entraîne un comportement indéterminé. Sans pouvoir voir le code, il est impossible de dire lequel. –

+3

Bien qu'il soit possible que vous ayez trouvé un bogue dans l'optimiseur, il est plus probable qu'il y ait un problème avec votre code (par exemple, s'appuyer sur la valeur d'une variable non initialisée). "travaux". Mais, tout cela n'est que spéculation sans voir le code réel. Essayez de le réduire au plus petit exemple qui démontre toujours le problème. –

Répondre

6

Toujours mettre votre code à travers quelque chose comme valgrind, Purify, etc, avant de blâmer l'optimiseur. Surtout quand on blâme les choses liées aux pointeurs. Cela ne veut pas dire que l'optimiseur n'est pas cassé, mais plus que probablement, c'est à vous de le faire. J'ai travaillé sur différents compilateurs C++ et vu ma part de failles qui ne se produisent qu'avec du code optimisé. Très souvent, les gens font des choses comme oublier de compter le \ 0 lors de l'allocation d'espace pour une chaîne, etc. Et c'est juste la chance à ce moment-là sur quelles pages vous êtes alloués lorsque le programme fonctionne avec des paramètres -O différents.

En outre, des questions importantes: avez-vous affaire à des pointeurs restreints?

0

Il semble que quelque chose accède à de la mémoire qu'il ne devrait pas avoir. Les symboles de débogage sont connus pour remettre à plus tard les mauvaises nouvelles.

Est-ce pur C ou y a-t-il quelque chose de fou comme un assemblage en ligne? Cependant, exécutez-le sur valgrind pour vérifier si cela peut se produire. Aussi, avez-vous essayé de compiler avec différents niveaux d'optimisation? Et sans débogage & optimisations?

1

Imprime le code d'assemblage généré par le compilateur, avec optimisations. Comparez à une liste en langage assembleur du code sans optimisations.

Le compilateur peut avoir compris que certaines des variables peuvent être éliminées. Ils n'ont pas été utilisés dans le calcul. Vous pouvez essayer de faire correspondre les esprits avec le compilateur et de factoriser les variables qui ne sont pas utilisées.

Le compilateur peut avoir substitué une boucle for par une équation. Dans certains cas (après avoir supprimé les variables inutilisées), la boucle peut être remplacée par une simple équation. Par exemple, une boucle qui ajoute 1 à une variable peut être remplacée par une instruction de multiplication.

Vous pouvez dire au compilateur à laisser une variable être en le déclarant comme volatile. Le mot clé volatile indique au compilateur que la valeur de la variable peut être modifiée par des moyens en dehors du programme et que le compilateur ne doit pas mettre en cache ni éliminer la variable. C'est une technique populaire dans la programmation de systèmes embarqués.

+0

Il y avait une question hier sur l'optimisation de casser quelque chose qui aurait dû être «volatile»: http://stackoverflow.com/questions/2603833/does-armcc-optimizes-non-volatile-variables-with-o0 –

1

Probablement votre programme exploite en quelque sorte un comportement indéfini qui fonctionne en votre faveur sans optimisation, mais avec l'optimisation -O3 il se retourne contre vous.

J'ai eu une expérience similaire avec un mon projet - cela fonctionne très bien avec -O2 mais rompt avec -O3. J'ai utilisé setjmp()/longjmp() lourdement dans mon code et j'ai dû faire la moitié des variables volatile pour le faire fonctionner alors j'ai décidé que -O2 est assez bon.

0

Sans code c'est difficile, mais voici quelques choses que j'ai déjà vues.

Le débogage des instructions d'impression finit souvent par être le seul utilisateur d'une valeur connue du compilateur. Sans l'instruction print, le compilateur pense qu'il peut se passer des opérations et des besoins en mémoire qui seraient autrement nécessaires pour calculer ou stocker cette valeur. Une chose similaire se produit lorsque vous avez des effets secondaires dans la liste d'arguments de votre instruction d'impression.

printf("%i %i\n", x, y = x - z); 

Un autre type d'erreur peut être:

for(i = 0; i < END; i++) { 
    int *a = &i; 
    foo(a); 
} 
if (bar) { 
    int * a; 
    baz(a); 
} 

Ce code aurait probablement le résultat escompté parce que le compilateur choisirait probablement de stocker à la fois une des variables dans le même endroit, de sorte que la seconde un serait avoir la dernière valeur que l'autre avait.

Les fonctions en ligne peuvent avoir un comportement étrange ou vous vous en doutez qu'elles ne sont pas alignées (ou parfois l'inverse), ce qui est souvent le cas pour du code non optimisé.

Vous devriez certainement essayer de compiler avec des avertissements mis au maximum (-Wall for gcc). Cela vous parlera souvent du code risqué.

(modifier) ​​ Juste pensé à un autre.

Si vous avez plusieurs façons de référencer une variable, vous pouvez avoir des problèmes qui fonctionnent correctement sans optimisation, mais qui se cassent lorsque l'optimisation est activée. Il y a deux façons principales cela peut arriver. Le premier est si une valeur peut être modifiée par un gestionnaire de signal ou un autre thread. Vous devez en informer le compilateur pour qu'il sache que tout accès suppose que la valeur doit être rechargée et/ou stockée. Ceci est fait en utilisant le mot clé volatile.

La seconde est un aliasing. C'est quand vous créez deux manières différentes d'accéder à la même mémoire. Les compilateurs sont généralement prompts à supposer que vous utilisez des pointeurs, mais pas toujours. En outre, ils sont des indicateurs d'optimisation pour certains qui leur disent d'être moins rapide pour faire ces hypothèses, ainsi que des moyens que vous pourriez tromper le compilateur (trucs fous comme while (foo != bar) { foo++; } *foo = x; n'étant pas évidemment une copie de bar à foo).