Optimisation gcc
Dernière réponse : dans Programmation
Bonjour,
Je n'arrive pas à comprendre en quoi le fait de compiler le code suivant avec -O2 puisse modifier sont comportement :
Avec -O1, le programme fait ce qu'on attend de lui, cad quand b atteins 32767+1, la boucle s'arrête. Avec -O2, b continue de croître au delà de la valeur max d'un short int..
Le code suivant donne le même résultat avec -O1 et -O2 :
Il doit certainement s'agir d'une subtilité documentée quelque part, mais je n'est pas trouvé.
Merci d'avance.
Je n'arrive pas à comprendre en quoi le fait de compiler le code suivant avec -O2 puisse modifier sont comportement :
#include <stdio.h> int main(void){ signed short int b=1; printf("%d\n",sizeof(b)); while(++b>0); return 0; }
Avec -O1, le programme fait ce qu'on attend de lui, cad quand b atteins 32767+1, la boucle s'arrête. Avec -O2, b continue de croître au delà de la valeur max d'un short int..
Le code suivant donne le même résultat avec -O1 et -O2 :
#include <stdio.h> int main(void){ signed short int b=32767; printf("%d\n",++b); return 0; }
Il doit certainement s'agir d'une subtilité documentée quelque part, mais je n'est pas trouvé.
Merci d'avance.
Autres pages sur : optimisation gcc
Lassé par la pub ? Créez un compte
Meilleure solution
Bon ben d'après le code assembleur, on à bien, avec -O2, une boucle infinie (ligne 18 et 19):
.file "test.c" .section .rodata.str1.1,"aMS",@progbits,1 .LC0: .string "%d\n" .text .p2align 4,,15 .globl main .type main, @function main: pushl %ebp movl $2, %eax movl %esp, %ebp andl $-16, %esp subl $16, %esp movl %eax, 4(%esp) movl $.LC0, (%esp) call printf .L2: jmp .L2 .size main, .-main .ident "GCC: (GNU) 4.4.2" .section .note.GNU-stack,"",@progbits
Oui mais bon, en logique, un signed int doit changer de signe après une certaine valeur.
Bref se méfier des optimisations... Il vaut mieux être explicite et faire un peut de zèle. En tout cas, ce n'est pas du tout le comportement que j'attends des optimisations. Je ne vais pas mètre -O2 pour compiler tout mes soft depuis les sources.
En tout cas, merci .
Bref se méfier des optimisations... Il vaut mieux être explicite et faire un peut de zèle. En tout cas, ce n'est pas du tout le comportement que j'attends des optimisations. Je ne vais pas mètre -O2 pour compiler tout mes soft depuis les sources.
En tout cas, merci .
Attention, tantal, entre ce que tu considères être de l'ordre du normal, de tes propres optimisations et de ce que tu attends du compilateur.
Sais-tu que lord d'une calcul, le compilateur transtype (cast) les opérandes au plus grand type ?
Du code est ajouté lors de la compilation pour que les débordements soient gérés, si on est en mode "optimisations faibles". Ce code est inutile si le programmeur sait exactement ce qu'il fait. On peut alors retirer ces contrôles. Les boucles vides, le code inaccessible (voir code), etc., sont virés.
Attention donc, l'analyse numérique a une vraie valeur ajoutée, et tout s'explique. Ne serait-il pas prétentieux de notre part de juger de la pertinence de ces "optimisations" sans nous remettre un peu en cause ?
Sais-tu que lord d'une calcul, le compilateur transtype (cast) les opérandes au plus grand type ?
Tu t'attends bien à obtenir un réel, pas un entier. Donc le calcul a bien été celui-là :
double deux_tiers = 2/3;
Donc le calcul
float deux_tiers = (float)2/(float)3;
signed short int b
++b>0
Devient++b>0
signed short int b
++(int)b>(int)0
puisque 0 est un int.++(int)b>(int)0
Du code est ajouté lors de la compilation pour que les débordements soient gérés, si on est en mode "optimisations faibles". Ce code est inutile si le programmeur sait exactement ce qu'il fait. On peut alors retirer ces contrôles. Les boucles vides, le code inaccessible (voir code), etc., sont virés.
Attention donc, l'analyse numérique a une vraie valeur ajoutée, et tout s'explique. Ne serait-il pas prétentieux de notre part de juger de la pertinence de ces "optimisations" sans nous remettre un peu en cause ?
{ printf("hello"); return; printf(" world"); }
Et même en croyant maitriser les optimisations, il existe toujours des différences entre plusieurs versions d'un même compilateur. Par exemple, compiler la fameuse racine carrée de karmack avec n'importe quel compilateur et n'importe quel optimisation, le résultat est totalement différent que ce à quoi on est attendu.
Bienvenue dans le monde de la programmation, ou ce qu'on croit maitriser ne représente que 50% de ce qui est fait réellement ^^
Bienvenue dans le monde de la programmation, ou ce qu'on croit maitriser ne représente que 50% de ce qui est fait réellement ^^
Oh, la ! DeathAngel. Doucement. Aux effets de bord près, heureusement que le résultat d'un code ne dépend pas du compilateur. Le C est quand même un langage déterministe ! Alors, oui, il y a des effets de bord. D'ailleurs, ce qu'étudiait tantal est justement un effet de bord : le bord des nombres entiers signés courts. ^^
Quant aux codes de J. Carmack, ça marche très bien. Les résultats sont approximatifs au sens mathématique, pas au sens informatique.
Par contre, je te rejoins quand tu dis que les optimisations varient d'un compilo à l'autre. Donc les effets de bord peuvent varier. L'art de bien programmer, c'est donc de toujours savoir se retrouver bien au milieu.
(Oki, Carmack est justement célèbre pour n'y jamais mettre les pieds
)
Quant aux codes de J. Carmack, ça marche très bien. Les résultats sont approximatifs au sens mathématique, pas au sens informatique.
Par contre, je te rejoins quand tu dis que les optimisations varient d'un compilo à l'autre. Donc les effets de bord peuvent varier. L'art de bien programmer, c'est donc de toujours savoir se retrouver bien au milieu.
(Oki, Carmack est justement célèbre pour n'y jamais mettre les pieds
)
Je comprend ces histoires de CAST, mais même un int peut déborder et devenir négatif avec une incrémentation. De plus, dire que 0 est vu comme un unsigned int, ne change pas la donne car si on compare à -1, le résultat est le même.
Donc, comme le dit deathangel67300, gcc considère l'incrémentation comme strictement croissante et "oubli" qu'il peut y avoir un débordement possible (bon OK, ce n'est pas une façon de coder
).
Je testerais tout de même avec ICC ce soir, juste pour voir.
Donc, comme le dit deathangel67300, gcc considère l'incrémentation comme strictement croissante et "oubli" qu'il peut y avoir un débordement possible (bon OK, ce n'est pas une façon de coder
).Je testerais tout de même avec ICC ce soir, juste pour voir.
zeb a dit :
Les résultats sont approximatifs au sens mathématique, pas au sens informatique.pour l'avoir testé sur GCC 4.1 et 4.2 l'époque, je peux te dire que c'était loin d'être approximatif au sens mathématique, on était même carrément à des valeurs délirantes.
Après d'autres cas ou tu touchais aux pointeurs devenaient aussi pas mal, par exemple, j'avais ca :
struct BitDart { unsigned int position :5; unsigned int valeur :27; }; int toto=34; BitDart * bd = &toto; bd->position=1; std::cout << toto << std::endl;
en GCC 4.2 -O0, il m'affichait 33 ce qui est le résultat attendu. -O2 me donnait 34. Le 4.3 34 en permanence, debug ou pas.
Bon d'accord, c'était pas le code super propre je l'avoue ^^ mais c'était le plus simple pour ce que j'avais à faire
J'aimerais bien connaitre les résultats de Tantal_fr avec ICC aussi.
tantal_fr a dit :
Je comprend ces histoires de CAST, mais même un int peut déborder et devenir négatif avec une incrémentation.deathangel67300 a dit :
pour l'avoir testé sur GCC 4.1 et 4.2 l'époque, je peux te dire que c'était loin d'être approximatif au sens mathématique, on était même carrément à des valeurs délirantes.Après d'autres cas ou tu touchais aux pointeurs devenaient aussi pas mal, par exemple, j'avais ca :
struct BitDart { unsigned int position :5; unsigned int valeur :27; }; int toto=34; BitDart * bd = &toto; bd->position=1; std::cout << toto << std::endl;
Et de manière générale, tu ne peux rien supposer sur la taille de ta structure. Elle pourrait très bien être sur 64bit, avec seulement une partie de 32bit utilisée. Surtout que les bitfields n'appartiennent à aucune norme C ou C++, donc tu n'a même pas de garantie que position est à coté de valeur.
Maintenant, quand on code un peu mieux (on à toujours des zones d'ombres, mais on dit un peu mieux ce qu'on veut faire) :
union realunion { struct { unsigned int position :5; unsigned int valeur :27; } dart; int i; } a = {.i = 34}; a.dart.position = 1;
et a.i deviens égal à 33, -O2 ou pas. (même si ça reste toujours pas standart)
Ah, tiens, ça me rappelle un truc : l'alignement.
Celui-ci peut se faire en 8, 16, 32, 64 bits. On peut le préciser, ou laisser faire les options par défaut. On se retrouve donc avec des struct ou des types standards définissant n octets, avec une fonction size qui te renvoie bien n, mais un emplacement mémoire dans le programme final égal à (n+64-1) modulo 64 dans le cas 64 bits, par exemple.
Oui, l'emplacement d'un char (1 octet), aligné sur un mot (word), sur un proco 64 bits, c'est 8 octets de réservés. Ça peut laisser de la place aux débordements !
Celui-ci peut se faire en 8, 16, 32, 64 bits. On peut le préciser, ou laisser faire les options par défaut. On se retrouve donc avec des struct ou des types standards définissant n octets, avec une fonction size qui te renvoie bien n, mais un emplacement mémoire dans le programme final égal à (n+64-1) modulo 64 dans le cas 64 bits, par exemple.
Oui, l'emplacement d'un char (1 octet), aligné sur un mot (word), sur un proco 64 bits, c'est 8 octets de réservés. Ça peut laisser de la place aux débordements !
batchy a dit :
C ou C++ ne défini pas le comportement à avoir en cas de débordement. Tu ne peux pas affirmer que ton int débordera de telle manière et deviendra négatif, c'est laissé à l'implémentation. Même si 99% du temps ça marche, ça n'est pas dans la norme.Là ça dépasse clairement mes compétences, enfin pour moi 0111 1111 + 1 = 1000 000. Je pensais que c'étais comme çà partout.
Sinon, je viens d'essayer avec ICC, et là quelques soit les optimisation, on ne tombe jamais en boucle infinie. Voila ce que ça donne en pseudo-asm avec -O2 :
# -- Machine type PW # mark_description "Intel(R) C++ Compiler Professional for applications running on IA-32, Version 11.0 Build 20081105 %s"; # mark_description "-long_double -O2 -S"; .file "test.c" .text ..TXTST0: # -- Begin main # mark_begin; .align 16,0x90 .globl main main: ..B1.1: # Preds ..B1.0 pushl %ebp #3.15 movl %esp, %ebp #3.15 andl $-128, %esp #3.15 subl $128, %esp #3.15 pushl $3 #3.15 call __intel_new_proc_init #3.15 # LOE ebx esi edi ..B1.5: # Preds ..B1.1 stmxcsr 4(%esp) #3.15 orl $32768, 4(%esp) #3.15 ldmxcsr 4(%esp) #3.15 pushl $2 #4.19 pushl $_2__STRING.0.0 #4.19 call printf #6.1 # LOE ebx esi edi ..B1.2: # Preds ..B1.5 xorl %eax, %eax #9.8 movl %ebp, %esp #9.8 popl %ebp #9.8 ret #9.8 .align 16,0x90 # LOE # mark_end; .type main,@function .size main,.-main .data # -- End main .section .rodata.str1.4, "aMS",@progbits,1 .align 4 .align 4 _2__STRING.0.0: .byte 37 .byte 100 .byte 10 .byte 0 .type _2__STRING.0.0,@object .size _2__STRING.0.0,4 .data .section .note.GNU-stack, "" # End
Là, la boucle a carrément sauté, ce qui est quand même plus logique, vue qu'au final, cette boucle ne fait rien que boucler sur elle même un certain nombre de fois.
Lassé par la pub ? Créez un compte
- Contenus similaires :
- ForumKit 5.1 Logitech Z5500 : Optimisation
- ForumOverclock I7 2600k + optimisation disque dur
- Forum[News] GCC 4.3
- Foruminstallation de gcc en tar.gz
- Forum[Résolu] Kubuntu : installation de gcc
- ForumProbleme avec gcc
- Forumpourquoi gcc-4.0.0 prend tant place?
- ForumPas de gcc :'(
- ForumAide optimisation
- Voir plus