[C++]1 / (x^2) ou (1 / x)^2 ?

En passant hier sur un site où il était question de benchmark, je me suis posé la question (me demandez pas d’où m’est venue la question, elle est là, c’est tout B) ) de savoir comment programmer au plus efficacement un calcul simple : 1/x^2; à savoir s’il vaut mieux écrire 1 / (x^2) ou (1 / x)^2

Me basant sur l’idée de simplement effectuer la même opération suffisamment souvent et comparer les temps d’exécution, voilà mon code, où j’ai sciemment utilisé deux variables temporaires en espérant que le compilateur n’optimise pas la chose automatiquement (le choix de 2.4 comme valeur de référence n’a pas été réfléchi outre mesure, si ce n’est qu’il n’est pas représentable exactement en binaire, ni son inverse) :

[code]#include <stdio.h>
#include <time.h>

int main() {
unsigned long int i;
const double greater = 2.4;
const double lesser = 1.0 / greater;
double tmp1, tmp2;
float t1, t2;

t1 = ((float)clock())/((float) CLOCKS_PER_SEC);
for (i = 0; i < 100000000; i++) {

 tmp1 = greater;
 tmp2 = 1.0 / tmp1;
 tmp1 = tmp2;
 tmp2 = tmp1 * tmp1;
}
t2 = ((float)clock())/((float) CLOCKS_PER_SEC);
t2 -= t1;
printf(« f/b time = %e seconds for 1st version\n », t2);

t1 = ((float)clock())/((float) CLOCKS_PER_SEC);
for (i = 0; i < 100000000; i++) {

 tmp1 = greater;
 tmp2 = tmp1 * tmp1;
 tmp1 = tmp2;
 tmp2 = 1.0 / tmp2;
}
t2 = ((float)clock())/((float) CLOCKS_PER_SEC);
t2 -= t1;
printf(« f/b time = %e seconds for 2nd version\n », t2);

t1 = ((float)clock())/((float) CLOCKS_PER_SEC);
for (i = 0; i < 100000000; i++) {

 tmp1 = lesser;
 tmp2 = 1.0 / tmp1;
 tmp1 = tmp2;
 tmp2 = tmp1 * tmp1;
}
t2 = ((float)clock())/((float) CLOCKS_PER_SEC);
t2 -= t1;
printf(« f/b time = %e seconds for 3rd version\n », t2);

t1 = ((float)clock())/((float) CLOCKS_PER_SEC);
for (i = 0; i < 100000000; i++) {

 tmp1 = lesser;
 tmp2 = tmp1 * tmp1;
 tmp1 = tmp2;
 tmp2 = 1.0 / tmp2;
}
t2 = ((float)clock())/((float) CLOCKS_PER_SEC);
t2 -= t1;
printf(« f/b time = %e seconds for 4th version\n », t2);
return 0;
}[/code]
Premier essai :
~/prog/ntp$ g++ -Wall -o bench bench.cpp
~/prog/ntp$ ./bench
f/b time = 2.180000e+00 seconds for 1st version
f/b time = 1.550000e+00 seconds for 2nd version
f/b time = 2.170000e+00 seconds for 3rd version
f/b time = 1.590000e+00 seconds for 4th version

Apparemment, il vaut mieux écrire 1 / (x^2), soit. Et ce quelque soit la valeur (> ou < que 1, pas essayé avec les négatifs toutefois, mais ça devrait rien changer).

J’essaie alors d’optimiser à peine la chose :
~/prog/ntp$ g++ -Wall -o bench -O1 bench.cpp
~/prog/ntp$ ./bench
f/b time = 1.500000e-01 seconds for 1st version
f/b time = 1.400000e-01 seconds for 2nd version
f/b time = 1.500000e-01 seconds for 3rd version
f/b time = 1.400000e-01 seconds for 4th version

Ah, il semblerait qu’une simple optimisation mette tout à niveau. Tout va bien, g++ semble correctement réagir, et surtout tout va plus vite, comme prévu B)
Mais en demandant d’optimiser encore plus la chose, voilà ce que j’obtiens (pour -O2 comme pour -O3 d’ailleurs) :
~/prog/ntp$ g++ -Wall -o bench -O2 bench.cpp
~/prog/ntp$ ./bench
f/b time = 1.400000e-01 seconds for 1st version
f/b time = 2.200000e-01 seconds for 2nd version
f/b time = 1.500000e-01 seconds for 3rd version
f/b time = 2.200000e-01 seconds for 4th version
:stuck_out_tongue: :stuck_out_tongue: :stuck_out_tongue: :stuck_out_tongue: (je pense que vous aurez compris que je suis resté très surpris) Les résultats sont exactement l’inverse de la version non optimisée (10x plus rapide tout de même, c’est au moins ça) !!! Quelqu’un a-t-il une idée pourquoi j’obtiens ce genre de résultats ??? Surtout qu’avec -O1 en option tout est à niveau !

a) jette un coup d’oeil au code genere, en asm pour chaque fonction, en incluant la boucle : ca peut faire toute la difference la boucle.
:stuck_out_tongue: essaye sans utiliser tmp, en faisant le calcul directement (a base de tmp = 1/(x*x)) ou un truc.
c) a essaye aussi : tu reutilises des variables (tmp2 = 1.0/tmp2), mais pas toujours : ca peut faire une difference a la compile. Essaye en utilisant une nouvelle var. temporaire pour chaque calcule

(et de toute facons : g++ sent des fesses :P, essaye avec un gcc 3.0.untruc ou un gcc.2.9.untruc aussi ! )

Trouve aussi un autre code pour tester le temps. De memoire, il n’y a que 18 TICKS PER SECONDE, ce qui ne fait pas beaucoup et surtout, c’est tres imprecis vu le temps ecoule. J’avais un code du timer, faudrait que je le retrouve pour le poster.

hein ? ou ca ? il me semblait que (en tout cas sur PC) le tick etait a la millisec ?

[quote name=‘c0unt0’ date=’ 10 Dec 2004, 00:29’]hein ? ou ca ? il me semblait que (en tout cas sur PC) le tick etait a la millisec ?
[right][post=“311604”]<{POST_SNAPBACK}>[/post][/right][/quote]
l’unité du tick est la milliseconde, mais il n’ya d’émission de ticks que toutes les 35 millisecondes ou un truc dans le genre. En gros le compteur de ticks passe de 0 à 35 ou de 35 à 70 en un seul coup… si tu tombes à la 69e milliseconde, et bien il te dit qu’il s’en est écoulé que 35… Mais il y a effectivement un code pour créer un timer plus efficace… mais je sais plus comment on fait.

Pour une mesure du temps plus précise, tu peux utiliser QueryPerformanceCounter(…) avec QueryPerformanceFrequency(…).