Bonjour,
Est-ce que quelqu'un saurait expliquer la difference de performance entre un build 32-bit et 64-bit de exp()?
Code:
#include <math.h>
#define SIZE (10000)
int main()
{
for (int i = 0; i < SIZE; ++i)
for (int j = 0; j < SIZE; ++j)
exp(i + j);
return 0;
}
Compilation (O3 ne change rien, j'utilise juste O0 pour eviter que le code ne soit completement saute):
$ g++ -o /tmp/test_exp_64 test_exp.cpp -Wall -O0
$ g++ -o /tmp/test_exp_32 test_exp.cpp -Wall -O0 -m32
$ time /tmp/test_exp_32; time /tmp/test_exp_64
real 0m24.235s
user 0m24.192s
sys 0m0.004s
real 0m2.764s
user 0m2.748s
sys 0m0.002s
En regardant l'assembleur genere on a - evidemment - des implementations tres differentes, mais je suis surpris par la difference de performance.
Comme je ne connais pas grand-chose au calcul numerique, est-ce que quelqu'un pourrait m'eclairer, et notamment donner une piste pour ameliorer la performance sur 32-bit ?
Merci d'avance !
# j'ai pas le code assembleur sous les yeux
Posté par fearan . Évalué à 4.
mais il est probable qu'il y ait des instructions spécifique au x86_64 permettant de faire quelques optimisations, alors que ton code 32bit a des chance d'être compilé pour du 386 ou 586, essaye d'activer manuellement les options pour ces archis
typiquement
-mtune=i686 ou voir
-march=i686 si jamais ça ne risque pas de tourner en dessous ou -march=pentium4 ou -march=athlon-4
mais bon un man gcc à la section Intel 386 and AMD x86-64 Options pour voir les archi possibles ou un cat /proc/cpuinfo | grep flags pour voir quels fonctionnalités sont dipos sur le proc en question. Attention cependant, -mtune permettra de tourner partout, c'est juste des optimisations spécifiques, -march va utiliser des instructions qui ne sont pas dispo sur tous les processeurs x86.
typiquement sur les proc pas antédéluviens -mfpmath=sse
Bref ça dépends de l'archi d'exécution. Les archis 64bits sont plus récente et ont forcément des jeux d'instruction qu'on est pas certain de retrouver sur les 32bits ce qui fait que par défaut plus d'options sont activées.
Il ne faut pas décorner les boeufs avant d'avoir semé le vent
[^] # Re: j'ai pas le code assembleur sous les yeux
Posté par neologix . Évalué à 2.
Oui, j'avais essaye avec differentes march/mtune, mais ca ne change rien…
# Registres
Posté par cfx . Évalué à 1.
Si ma mémoire est bonne, en 64-bits tu as (évidemment) accès à des registres plus grand, mais aussi plus nombreux.
Les 64-bits te permettent ainsi de faire des calculs numériques plus précis (et donc potentiellement d'en faire moins), tout en bénéficiant d'une meilleure localité des données du fait de la présence de plus de registres.
Comment améliorer les performances en 32-bits ? Passer en 64-bits !
Sinon, tu peux toujours essayer de te recompiler la lib C en O3 (j'imagine qu'elle est compilée en O2), mais il est assez probable que le gain soit ridicule.
# Commentaire supprimé
Posté par Anonyme . Évalué à 1.
Ce commentaire a été supprimé par l’équipe de modération.
[^] # Re: Libc différentes
Posté par Graveen . Évalué à 2.
J'aurais avancé peu ou prou la même chose. Si tu as un kernel x64 (ce qui est le cas puisque tu executes du code x64), il y a peut-être des options de retro compatibilité, ce qui nous amènerait au fait que le x64 execute mal le 32bits, et qu'il vaut mieux privilégier l'architecture native :)
J'imagine que faire tourner ton code sur un live usb amd64 vs i686 doit permettre d'avoir une idée plus précise.
[^] # Re: Libc différentes
Posté par neologix . Évalué à 2.
Merci, mais c'est évident que les versions 32-bit et 64-bit sont linké avec des libm différentes (/lib vs /lib64).
Ce que j'aimerais savoir c'est pourquoi la version 32-bit est tellement lente.
[^] # Commentaire supprimé
Posté par Anonyme . Évalué à 1.
Ce commentaire a été supprimé par l’équipe de modération.
[^] # Re: Libc différentes
Posté par neologix . Évalué à 1.
Non mais en tourne en rond là, c'est évident, ce qui m'intéresse c'est pourquoi la version 32 bit est si lente (je compile uniquement en -O0 pour que gcc n'optimise pas complètement la boucle, étant donné que je n'utilise pas les valeurs retournées par exp() et que c'est une fonction pure).
[^] # Re: Libc différentes
Posté par Zylabon . Évalué à 2.
Ben, on t'a répondu…
Regarde les implémentations, c'est pas les mêmes. Si tu veux que quelqu'un le fasse pour toi, je comprend, c'est pas évident de s'y retrouver dans la glibc, demande, mais poser encore et encore la même question n'est pas une bonne stratégie.
Tu peux empêcher gcc de faire l'optimisation en faisant un truc à la con avec le résultat de exp, genre l'accumuler dans une variable et le retourner à la fin. Mais ça changera rien parce que la lib n'est pas recompilée, tu aura toujours une différence énorme.
Please do not feed the trolls
[^] # Re: Libc différentes
Posté par neologix . Évalué à 1.
Comme je l'ai dit il est évident que les implémentations sont différentes et que la différence vient de là, je voulais savoir si quelqu'un avait une idée plus précise du pourquoi une telle différence de performance (voir ci-dessous, c'est probablement parce qu'on atteint un range error sur 32-bit). J'ai déjà jeté un oeil à l'assembleur, mais je n'avais rien vu d'évident (mis à part que les implémentations n'ont rien à voir).
D'ailleurs, en y repensant, l'appel à la fonction ne peut pas être optimisé, parce qu'elle n'est pas pure (elle peut changer errno).
[^] # Commentaire supprimé
Posté par Anonyme . Évalué à 2.
Ce commentaire a été supprimé par l’équipe de modération.
# overflow
Posté par neologix . Évalué à 9.
C'est visiblement lié à un overflow/domain représentable par un double sur 32-bit.
En changeant l'appel à exp() en :
exp((i + j) / (double)SIZE);
Les performances sont comparables.
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.