Curieux aussi cette nécessité d'optimiser pour valider l'option -ffloat-store,
-ffloat-store contraint l'optimisation. C'est donc pas etonnant qu'il faille optimiser pour remarquer les differences induites dans l'optimisation.
A+ -- Jean-Marc FAQ de fclc: http://www.levenez.com/lang/c/faq Site de usenet-fr: http://www.usenet-fr.news.eu.org
Vincent Lefevre
Dans l'article , Kojak écrit:
Le Tue, 31 Mar 2009 10:21:37 +0000 (UTC), Vincent Lefevre a écrit :
> Non, -ffloat-store a bien un effet avec GCC 3.x, mais contrairement > à GCC 4.x, *uniquement* au niveau des affectations (même pas pour > les casts). Lorsqu'on compile sans optimisation, ce n'est pas > visible, car chaque affectation a pour effet de copier le résultat > en mémoire, mais avec les optimisations, les résultats sont gardés > par défaut dans les registres (ce qui est au passage un bug si le > type correspondant au registre est "plus gros" que le type de la > variable - bug 323 de GCC).
Bizarre autant qu'étrange ces comportements de GCC. Curieux aussi cette nécessité d'optimiser pour valider l'option -ffloat-store,
Disons que l'option -ffloat-store telle qu'elle est documentée ne devrait pas exister en C, puisque les store sont obligatoires lors des affectations (sauf si le compilo peut prouver qu'ils ne changent pas le résultat). Sans optimisation, les stores sont toujours générés par le compilo, d'où la nécessité d'optimiser pour voir l'effet du -ffloat-store (sans optimisation, le code généré reste correct).
sans compter les incohérences entre les différentes versions.
Oui. Les stores à l'intérieur d'une expression (générés par GCC 4.x) ne sont pas nécessaires. D'une part, la norme C ne les impose pas, et d'autre part, si le but est d'effectuer tous les calculs à la précision (et plage d'exposants) des types utilisés, ces stores ne règlent pas le problème du double arrondi. Ils peuvent être bénéfiques à certains codes (sans aucune garantie et souvent sans possibilité de le prouver), mais inversement, le double arrondi peut avoir pour effet d'augmenter la borne d'erreur par rapport à ce que garantit la norme C: l'arrondi au plus près permet d'obtenir une borne d'erreur de 1/2 ulp (qui reste correcte lorsque les calculs intermédiaires sont faits à une plus grande précision), mais en cas de double arrondi, cette borne devient 1/2 ulp + 1/2 ulp' où ulp' est l'ulp correspondant à la précision intermédiaire.
Note: si un double arrondi possible est prévu par la norme, e.g. lors d'une affectation, celui qui fait l'analyse d'erreur doit de toute façon le prendre en compte.
Dans l'article <20090401064100.02f6d00e@thor.janville.org>,
Kojak <nntpspy@janville.borg.invalid> écrit:
Le Tue, 31 Mar 2009 10:21:37 +0000 (UTC),
Vincent Lefevre a écrit :
> Non, -ffloat-store a bien un effet avec GCC 3.x, mais contrairement
> à GCC 4.x, *uniquement* au niveau des affectations (même pas pour
> les casts). Lorsqu'on compile sans optimisation, ce n'est pas
> visible, car chaque affectation a pour effet de copier le résultat
> en mémoire, mais avec les optimisations, les résultats sont gardés
> par défaut dans les registres (ce qui est au passage un bug si le
> type correspondant au registre est "plus gros" que le type de la
> variable - bug 323 de GCC).
Bizarre autant qu'étrange ces comportements de GCC. Curieux aussi
cette nécessité d'optimiser pour valider l'option -ffloat-store,
Disons que l'option -ffloat-store telle qu'elle est documentée ne
devrait pas exister en C, puisque les store sont obligatoires lors
des affectations (sauf si le compilo peut prouver qu'ils ne changent
pas le résultat). Sans optimisation, les stores sont toujours générés
par le compilo, d'où la nécessité d'optimiser pour voir l'effet du
-ffloat-store (sans optimisation, le code généré reste correct).
sans compter les incohérences entre les différentes versions.
Oui. Les stores à l'intérieur d'une expression (générés par GCC 4.x)
ne sont pas nécessaires. D'une part, la norme C ne les impose pas,
et d'autre part, si le but est d'effectuer tous les calculs à la
précision (et plage d'exposants) des types utilisés, ces stores ne
règlent pas le problème du double arrondi. Ils peuvent être bénéfiques
à certains codes (sans aucune garantie et souvent sans possibilité
de le prouver), mais inversement, le double arrondi peut avoir pour
effet d'augmenter la borne d'erreur par rapport à ce que garantit la
norme C: l'arrondi au plus près permet d'obtenir une borne d'erreur
de 1/2 ulp (qui reste correcte lorsque les calculs intermédiaires sont
faits à une plus grande précision), mais en cas de double arrondi,
cette borne devient 1/2 ulp + 1/2 ulp' où ulp' est l'ulp correspondant
à la précision intermédiaire.
Note: si un double arrondi possible est prévu par la norme, e.g. lors
d'une affectation, celui qui fait l'analyse d'erreur doit de toute
façon le prendre en compte.
Le Tue, 31 Mar 2009 10:21:37 +0000 (UTC), Vincent Lefevre a écrit :
> Non, -ffloat-store a bien un effet avec GCC 3.x, mais contrairement > à GCC 4.x, *uniquement* au niveau des affectations (même pas pour > les casts). Lorsqu'on compile sans optimisation, ce n'est pas > visible, car chaque affectation a pour effet de copier le résultat > en mémoire, mais avec les optimisations, les résultats sont gardés > par défaut dans les registres (ce qui est au passage un bug si le > type correspondant au registre est "plus gros" que le type de la > variable - bug 323 de GCC).
Bizarre autant qu'étrange ces comportements de GCC. Curieux aussi cette nécessité d'optimiser pour valider l'option -ffloat-store,
Disons que l'option -ffloat-store telle qu'elle est documentée ne devrait pas exister en C, puisque les store sont obligatoires lors des affectations (sauf si le compilo peut prouver qu'ils ne changent pas le résultat). Sans optimisation, les stores sont toujours générés par le compilo, d'où la nécessité d'optimiser pour voir l'effet du -ffloat-store (sans optimisation, le code généré reste correct).
sans compter les incohérences entre les différentes versions.
Oui. Les stores à l'intérieur d'une expression (générés par GCC 4.x) ne sont pas nécessaires. D'une part, la norme C ne les impose pas, et d'autre part, si le but est d'effectuer tous les calculs à la précision (et plage d'exposants) des types utilisés, ces stores ne règlent pas le problème du double arrondi. Ils peuvent être bénéfiques à certains codes (sans aucune garantie et souvent sans possibilité de le prouver), mais inversement, le double arrondi peut avoir pour effet d'augmenter la borne d'erreur par rapport à ce que garantit la norme C: l'arrondi au plus près permet d'obtenir une borne d'erreur de 1/2 ulp (qui reste correcte lorsque les calculs intermédiaires sont faits à une plus grande précision), mais en cas de double arrondi, cette borne devient 1/2 ulp + 1/2 ulp' où ulp' est l'ulp correspondant à la précision intermédiaire.
Note: si un double arrondi possible est prévu par la norme, e.g. lors d'une affectation, celui qui fait l'analyse d'erreur doit de toute façon le prendre en compte.
Bizarre autant qu'étrange ces comportements de GCC.
Je ne trouve pas. Tout ceci vient d'une bonne raison : faire les choses « correctement » à la fois coûte en perfs' et n'a pas d'effet notable pour 9x% des utilisateurs de GCC (x étant une v.a. variant entre 0 et 9,9999). À partir de là, les développeurs de GCC ne se sont pas encore fait une religion (en fait ils s'en étaient faite une dans le bug 323, mais ils sont revenus dessus), et ils louvoient, implémentant certaines choses qui sont effectivement choquantes, et laissant de côté d'autres qui semblent plus anodins... jusqu'au jour où cela revient leur sauter à la figure.
Il faut bien voir que c'est un classique du respect d'une norme : tant que cela ne coûte rien ou presque, il n'y a de problème pour personne à partir du moment où le problème est détecté ; ainsi, le fait qu'un résultat entier intermédiaire stocké dans un unsigned char ou transtypé en unsigned short (éventuellement au travers d'un appel de fonction) doivent être traduit par le compilateur comme une opération de masquage si l'optimiseur a déterminé qu'il n'y a pas besoin de stockage intermédiaire a pris quelques années vers 1990, mais maintenant c'est bien acquis (notons au passage qu'un problème du même acabit a fait exploser une fusée...) Si l'opération est chère, il y a déjà nettement moins d'enthousiasme : c'est ainsi qu'en C, les architectures en complément à 1 ou signe+magnitude ne sont pas obligées de simuler le comportement du complément à 2 (il y a d'autres langages où elles le sont), car le faire les rendraient inopérantes. Idem pour la taille de ptrdiff_t: normalement la mantisse de ce type devrait être un bit plus grande que la taille des pointeurs, donc que la taille de size_t ; en pratique cela obligerait beaucoup d'architectures à utiliser long long pour ptrdiff_t, et là on voit bien que cela coulerait immédiatement l'intérêt du type, qui est déjà sous-utilisé : aussi l'on s'accommode d'un ptrdiff_t rabougri, et on fait semblant d'ignorer les problèmes qui vont survenir maintenant que certaines applis 32 bits flirtent avec les 4Go, ou on fait confiance à l'arithmétique en complément à 2 pour en réduire les effets (ce qui est une attitude carrément dangereuse).
Pour en revenir à nos flottants, d'abord il faut voir que le cas à problème est plutôt rare, il faut soit une machine avec précision étendue (typiquement x87) mais où l'on veut avoir les mêmes résultats qu'en double, soit un code qui utilise de la simple précision voire un mélange entre les deux précisions : dans les deux cas, on a affaire à des spécialistes de Q-v. Ensuite, on est dans un monde assis entre plusieurs chaises, où en plus des développeurs de GCC et des spécialistes numériques, on a les responsables de la libc ou de l'architecture processeur, qui ne veulent pas particulièrement entendre parler de modification drastique de drapeaux de contrôle du NDP87, ou qui veulent promouvoir leur dernier bébé (genre SSE35)...
Je finirais par une petite remarque : je pense que si à l'origine les deux architectures t'avaient donné(e) le même résultat, tu n'aurais pas trouvé cela aussi étrange ou bizarre, MÊME si le dit résultat avait été 99 : au plus tu aurais cherché un peu à comprendre pourquoi tu avais ce résultat, et tu aurais eu l'explication très rapidement ; c'est l'occurrence des deux résultats différents qui te préoccupe. Mais il faut alors se poser une question : quelle est l'importance d'avoir deux résultats possibles, si l'opération initiale est erronée ?
Antoine
Le 01/04/2009 04:41Z, Kodak écrivit :
Bizarre autant qu'étrange ces comportements de GCC.
Je ne trouve pas.
Tout ceci vient d'une bonne raison : faire les choses « correctement » à
la fois coûte en perfs' et n'a pas d'effet notable pour 9x% des
utilisateurs de GCC (x étant une v.a. variant entre 0 et 9,9999).
À partir de là, les développeurs de GCC ne se sont pas encore fait une
religion (en fait ils s'en étaient faite une dans le bug 323, mais ils
sont revenus dessus), et ils louvoient, implémentant certaines choses
qui sont effectivement choquantes, et laissant de côté d'autres qui
semblent plus anodins... jusqu'au jour où cela revient leur sauter à la
figure.
Il faut bien voir que c'est un classique du respect d'une norme : tant
que cela ne coûte rien ou presque, il n'y a de problème pour personne à
partir du moment où le problème est détecté ; ainsi, le fait qu'un
résultat entier intermédiaire stocké dans un unsigned char ou transtypé
en unsigned short (éventuellement au travers d'un appel de fonction)
doivent être traduit par le compilateur comme une opération de masquage
si l'optimiseur a déterminé qu'il n'y a pas besoin de stockage
intermédiaire a pris quelques années vers 1990, mais maintenant c'est
bien acquis (notons au passage qu'un problème du même acabit a fait
exploser une fusée...)
Si l'opération est chère, il y a déjà nettement moins d'enthousiasme :
c'est ainsi qu'en C, les architectures en complément à 1 ou
signe+magnitude ne sont pas obligées de simuler le comportement du
complément à 2 (il y a d'autres langages où elles le sont), car le faire
les rendraient inopérantes. Idem pour la taille de ptrdiff_t:
normalement la mantisse de ce type devrait être un bit plus grande que
la taille des pointeurs, donc que la taille de size_t ; en pratique cela
obligerait beaucoup d'architectures à utiliser long long pour ptrdiff_t,
et là on voit bien que cela coulerait immédiatement l'intérêt du type,
qui est déjà sous-utilisé : aussi l'on s'accommode d'un ptrdiff_t
rabougri, et on fait semblant d'ignorer les problèmes qui vont survenir
maintenant que certaines applis 32 bits flirtent avec les 4Go, ou on
fait confiance à l'arithmétique en complément à 2 pour en réduire les
effets (ce qui est une attitude carrément dangereuse).
Pour en revenir à nos flottants, d'abord il faut voir que le cas à
problème est plutôt rare, il faut soit une machine avec précision
étendue (typiquement x87) mais où l'on veut avoir les mêmes résultats
qu'en double, soit un code qui utilise de la simple précision voire un
mélange entre les deux précisions : dans les deux cas, on a affaire à
des spécialistes de Q-v.
Ensuite, on est dans un monde assis entre plusieurs chaises, où en plus
des développeurs de GCC et des spécialistes numériques, on a les
responsables de la libc ou de l'architecture processeur, qui ne veulent
pas particulièrement entendre parler de modification drastique de
drapeaux de contrôle du NDP87, ou qui veulent promouvoir leur dernier
bébé (genre SSE35)...
Je finirais par une petite remarque : je pense que si à l'origine les
deux architectures t'avaient donné(e) le même résultat, tu n'aurais pas
trouvé cela aussi étrange ou bizarre, MÊME si le dit résultat avait été
99 : au plus tu aurais cherché un peu à comprendre pourquoi tu avais ce
résultat, et tu aurais eu l'explication très rapidement ; c'est
l'occurrence des deux résultats différents qui te préoccupe. Mais il
faut alors se poser une question : quelle est l'importance d'avoir deux
résultats possibles, si l'opération initiale est erronée ?
Bizarre autant qu'étrange ces comportements de GCC.
Je ne trouve pas. Tout ceci vient d'une bonne raison : faire les choses « correctement » à la fois coûte en perfs' et n'a pas d'effet notable pour 9x% des utilisateurs de GCC (x étant une v.a. variant entre 0 et 9,9999). À partir de là, les développeurs de GCC ne se sont pas encore fait une religion (en fait ils s'en étaient faite une dans le bug 323, mais ils sont revenus dessus), et ils louvoient, implémentant certaines choses qui sont effectivement choquantes, et laissant de côté d'autres qui semblent plus anodins... jusqu'au jour où cela revient leur sauter à la figure.
Il faut bien voir que c'est un classique du respect d'une norme : tant que cela ne coûte rien ou presque, il n'y a de problème pour personne à partir du moment où le problème est détecté ; ainsi, le fait qu'un résultat entier intermédiaire stocké dans un unsigned char ou transtypé en unsigned short (éventuellement au travers d'un appel de fonction) doivent être traduit par le compilateur comme une opération de masquage si l'optimiseur a déterminé qu'il n'y a pas besoin de stockage intermédiaire a pris quelques années vers 1990, mais maintenant c'est bien acquis (notons au passage qu'un problème du même acabit a fait exploser une fusée...) Si l'opération est chère, il y a déjà nettement moins d'enthousiasme : c'est ainsi qu'en C, les architectures en complément à 1 ou signe+magnitude ne sont pas obligées de simuler le comportement du complément à 2 (il y a d'autres langages où elles le sont), car le faire les rendraient inopérantes. Idem pour la taille de ptrdiff_t: normalement la mantisse de ce type devrait être un bit plus grande que la taille des pointeurs, donc que la taille de size_t ; en pratique cela obligerait beaucoup d'architectures à utiliser long long pour ptrdiff_t, et là on voit bien que cela coulerait immédiatement l'intérêt du type, qui est déjà sous-utilisé : aussi l'on s'accommode d'un ptrdiff_t rabougri, et on fait semblant d'ignorer les problèmes qui vont survenir maintenant que certaines applis 32 bits flirtent avec les 4Go, ou on fait confiance à l'arithmétique en complément à 2 pour en réduire les effets (ce qui est une attitude carrément dangereuse).
Pour en revenir à nos flottants, d'abord il faut voir que le cas à problème est plutôt rare, il faut soit une machine avec précision étendue (typiquement x87) mais où l'on veut avoir les mêmes résultats qu'en double, soit un code qui utilise de la simple précision voire un mélange entre les deux précisions : dans les deux cas, on a affaire à des spécialistes de Q-v. Ensuite, on est dans un monde assis entre plusieurs chaises, où en plus des développeurs de GCC et des spécialistes numériques, on a les responsables de la libc ou de l'architecture processeur, qui ne veulent pas particulièrement entendre parler de modification drastique de drapeaux de contrôle du NDP87, ou qui veulent promouvoir leur dernier bébé (genre SSE35)...
Je finirais par une petite remarque : je pense que si à l'origine les deux architectures t'avaient donné(e) le même résultat, tu n'aurais pas trouvé cela aussi étrange ou bizarre, MÊME si le dit résultat avait été 99 : au plus tu aurais cherché un peu à comprendre pourquoi tu avais ce résultat, et tu aurais eu l'explication très rapidement ; c'est l'occurrence des deux résultats différents qui te préoccupe. Mais il faut alors se poser une question : quelle est l'importance d'avoir deux résultats possibles, si l'opération initiale est erronée ?