L'une des choses qui amuse beaucoup le bidouilleur, c'est de faire preuve d'un maximum de ruse pour faire aller plus vite son programme. Pour mener à bien cette tache, le bidouilleur dispose en gros de trois techniques :
La première méthode est la plus noble, et bien souvent la plus difficile. En effet elle nécessite souvent de ne pas passer son temps devant sa machine, mais dans les livres. Elle est malheureusement très souvent délaissé par les bidouilleurs.
La deuxième méthode tient plus de la bonne maitrise du langage de programmation. L'expérience et la lecture régulière de livre, de magazine et de code produit par d'autre personne permet d'améliorer cela.
La dernière méthode est très souvent la méthode la plus utilisée, et je souhaites vous démontrer à tord. Elle est la plus utilisér car c'est bien souvent celle qui permet le plus de s'approcher du mythe du bidouilleur qui passe ses nuits sur sa machine à améliorer des programmes.
Dans notre cas, nous allons prendre pour exemple un programme de calcul de l'ensemble de Mandelbrot. En général, un programme de calcul de l'ensemble de Mandelbrot, ça ressemble à cela:
mandelbrot.c |
---|
/* Programme de bench calcul de l'ensemble de Mandelbrot Emmanuel DUMAS (emmanuel.dumas@free.fr) 25/10/2001 */ /* il y a volontairement aucun include, cela simplifie la compilation*/ #define XMIN -2.5 #define XMAX 1.5 #define YMIN -1.5 #define YMAX 1.5 #define NMAX 1024 #define SEUIL 4.0 #define LX 1024 #define LY 768 #ifndef NUMBER #define NUMBER double #endif /* Fonction de calcul de l'ensemble de Mandelbrot rappel du principe : on calcule la suite zn+1=zn²+z0 si la suite diverge, le point n'appartient pas à l'ensemble sinon il appartient */ int mandelpoint(NUMBER z0_r, NUMBER z0_i, int nmax) { int cp=0; NUMBER zn_r,zn_i,a,b; /* initialisation */ zn_r=z0_r; zn_i=z0_i; a=zn_r*zn_r; b=zn_i*zn_i; /* et on fait tourner le tout */ while ( (cp<nmax) &&( (a+b) < SEUIL ) ) { zn_i=2.0*zn_r*zn_i+z0_i; zn_r=a-b+z0_r; a=zn_r*zn_r; b=zn_i*zn_i; cp++; } return cp; } /* Pour obtenir une image on calcul cette fct pour tout un plan */ void mandelbrot(const NUMBER xmin, const NUMBER xmax, const NUMBER ymin, const NUMBER ymax, const int lx, const int ly, const int nmax, int *res) { int x,y; for(x=0;x<lx;x++) for(y=0;y<ly;y++) { NUMBER z0_r,z0_i; /* calcul de z0 */ z0_r=xmin+(xmax-xmin)*x/lx; z0_i=ymin+(ymax-ymin)*y/ly; /* on sauve le résultat */ res[x+y*lx]=mandelpoint(z0_r,z0_i,nmax); } } main() { int i; /* On fait le calcul 10 fois pour moyenner le résultat */ for(i=0;i<10;i++) { int res[LX*LY]; mandelbrot(XMIN,XMAX,YMIN,YMAX,LX,LY,NMAX,res); } return 0; } |
Sur un PC équipé d'un processus Athlon 850 Mhz, on obtient en fonction des options de compilation, les temps d'executions suivant:
option de compilation | temps d'execution |
---|---|
aucune | 46s29 |
-O1 | 23s94 |
-O2 | 22s42 |
-O3 | 23s88 |
Pour notre deuxième étape nous allons prendre un programme de calcul de l'ensemble de Mandelbrot en priviliégeant la lisibilité du code à la performance, ce qui donne ceci :
mandelbrot_2.c |
---|
/* Programme de bench calcul de l'ensemble de Mandelbrot Emmanuel DUMAS (emmanuel point dumas arobase free point france) 25/10/2001 première version 05/11/2001 version pas du tout optimisée */ /* il y a volontairement aucun include, cela simplifie la compilation*/ #define XMIN -2.5 #define XMAX 1.5 #define YMIN -1.5 #define YMAX 1.5 #define NMAX 1024 #define SEUIL 10.0 #define LX 1024 #define LY 768 #ifndef NUMBER #define NUMBER double #endif NUMBER module(NUMBER r,NUMBER i) { return r*r+i*i; } /* Fonction de calcul de l'ensemble de Mandelbrot rappel du principe : on calcule la suite zn+1=zn²+z0 si la suite diverge, le point n'appartient pas à l'ensemble sinon il appartient */ int mandelpoint(NUMBER z0_r, NUMBER z0_i, int nmax) { int cp=0; NUMBER zn_r,zn_i; /* initialisation */ zn_r=z0_r; zn_i=z0_i; /* et on fait tourner le tout */ while ( (cp<nmax) &&( module(zn_r,zn_i) < SEUIL ) ) { NUMBER zn1_r,zn1_i; /* zn+1=zn^2+z0 */ zn1_r= zn_r*zn_r - zn_i*zn_i + z0_r; zn1_i= 2.0*zn_r*zn_i + z0_i; zn_r=zn1_r; zn_i=zn1_i; cp++; } return cp; } /* Pour obtenir une image on calcul cette fct pour tout un plan */ void mandelbrot(const NUMBER xmin, const NUMBER xmax, const NUMBER ymin, const NUMBER ymax, const int lx, const int ly, const int nmax, int *res) { int x,y; for(x=0;x<lx;x++) for(y=0;y<ly;y++) { NUMBER z0_r,z0_i; /* calcul de z0 */ z0_r=xmin+(xmax-xmin)*x/lx; z0_i=ymin+(ymax-ymin)*y/ly; /* on sauve le résultat */ res[x+y*lx]=mandelpoint(z0_r,z0_i,nmax); } } main() { int i; /* On fait le calcul 10 fois pour moyenner le résultat */ for(i=0;i<10;i++) { int res[LX*LY]; mandelbrot(XMIN,XMAX,YMIN,YMAX,LX,LY,NMAX,res); } return 0; } |
Avec ce programme, toujours sur le même PC équipé d'un processus Athlon 850 Mhz, on obtient en fonction des options de compilation, les temps d'executions suivant:
option de compilation | temps d'execution du programme précédent (pour mémoire) | temps d'execution |
---|---|---|
aucune | 46s29 | 110s20 |
-O1 | 23s94 | 74s03 |
-O2 | 22s42 | 69s36 |
-O3 | 23s88 | 25s14 |
Sur cette exemple on constate qu'avec une optimisation -O3, les deux programmes sont à une vitesse comparable.
A de très rare exception près, quand on écrit du code, il faut priviliéger la lisibilité du code à son optimisation. Les compilateurs modernes sont beaucoup plus aptes à optimiser le code que beaucoup de developpeurs actuels.