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
(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 !