Un bench mesure toujours un paramètre très précis de la machine. Il est claire que dans la vrai vie ça n'est pas aussi simple, donc prenez avec précaution les conclusions qui vont suivre. Ce n'est pas parce-qu'un bench dit qu'une option de compilation multiplie par deux la vitesse d'une fonction qu'il suffit de recompiler tous ses programmes de la vraie vie pour constater un gain en vitesse d'un facteur de deux. Si c'était si simple, ça se saurait :) .
Avant de se lancer dans des tests compliqués, il faut choisir un programme test et s'y tenir. Pour ma part j'ai réecrit un petit programme de calcul de l'ensemble de Mandelbrot. On peut trouver d'autres programmes pratiques sur le site ftp de l'Inria.
Donc voici mon programme de test :
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; } |
Ce petit programme se compile très facilement. On suppose que le code source se trouve dans le fichier mandelbrot.c, la ligne de commande standard pour le compiler est :
Ready cc mandelbrot.c -o mandelbrot |
Ensuite pour l'executer, il suffit de tapper :
Ready ./mandelbrot Ready |
Seulement il ne faut oublier qu'arrivé à ce stade pour l'instant, il ne se passe pas grand chose.
Pour aller plus loin il faut connaitre la commande
Ready man time TIME(1) TIME(1) NAME time - time a simple command or give resource usage SYNOPSIS time [options] command [arguments...] DESCRIPTION The time command runs the specified program command with the given arguments. When command finishes, time writes a message to standard output giving timing statistics about this program run. These statistics consist of (i) the elapsed real time between invocation and termination, (ii) the user CPU time (the sum of the tms_utime and tms_cutime values in a struct tms as returned by times(2)), and (iii) the system CPU time (the sum of the tms_stime and tms_cstime values in a struct tms as returned by times(2)). ... |
Et si on l'essaye sur notre programme voici ce que l'on obtient :
Ready time ./mandelbrot 43.59user 0.08system 0:43.95elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready |
Et ici on a le détail des ressources consommées. C'est un peu le fouilli, mais en gros on voit que le programme à consommer 43,59 secondes de CPU en user et 0,08 seconde de CPU en system, il s'est écoulé 43,95 secondes entre le début et la fin du programme, ce qui fait que le programme à consommer 99% du CPU pendant cette période.
L'interêt du programme time c'est qu'il donne les ressources réellement consommées ce qui est plus fiable que le délai qui s'est écoulé entre le début et la fin du programme. En effet la plupart des machines sont multitaches, et on ne se rend plus forcément compte que l'on a plusieurs programmes qui tournent et qui font varier la durée totale d'éxécution. Ainsi par exemple j'ai relancer la même commande mais en veillant à faire tourner un autre programme simultannemment.
Ready time ./mandelbrot 44.81user 0.14system 1:14.62elapsed 60%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready |
On constate que la consommation CPU est un peu près la même, mais que le temps total d'execution est bien plus long.
Passons à des choses un peu plus sérieuse. Comme on l'a dit, on va
utiliser la commande
Ainsi pour nos deux précédents, on dirait que le programme a mis respectivement 43,67 secondes et 44,95 secondes. Il est vrai qu'il un peu pénible de ne pas toujours avoir le même temps, mais les spécialistes des microprocesseurs vous expliqueront que sur les machines actuelles avec toutes les techniques de caches et de pipelines, c'est très dure de reproduire deux fois exactement le même phénomène.
Maintenant rentrons dans le coeur du sujet, c'est à dire les options de compilation.
Notre programme test a été compilé pour notre premier test de la manière suivante :
Ready cc mandelbrot.c -o mandelbrot |
Comme tout bon développeur sait, le compilateur a une multitude
d'option de compilation. Ces options dépendant énormement des
compilateurs utilisés, on va supposer que l'on utilise le compilateur
Dans un premier temps on va se contenter d'étudier l'effet de l'option -Ox ou x indique le niveau d'optimisation souhaiter. Dans un premier temps, le reflexe consiste bien sur a regarder ce que dit le man :
Ready man gcc ... These options control various sorts of optimizations: -O -O1 Optimize. Optimizing compilation takes somewhat more time, and a lot more memory for a large function. Without -O, the compiler's goal is to reduce the cost of compilation and to make debugging produce the expected results. Statements are independent: if you stop the program with a breakpoint between statements, you can then assign a new value to any variable or change the program counter to any other statement in the function and get exactly the results you would expect from the source code. Without -O, the compiler only allocates variables declared "register" in registers. The resulting com piled code is a little worse than produced by PCC without -O. With -O, the compiler tries to reduce code size and execution time. When you specify -O, the compiler turns on -fthread- jumps and -fdefer-pop on all machines. The compiler turns on -fdelayed-branch on machines that have delay slots, and -fomit-frame-pointer on machines that can support debugging even without a frame pointer. On some machines the compiler also turns on other flags. -O2 Optimize even more. GCC performs nearly all supported optimizations that do not involve a space-speed trade off. The compiler does not perform loop unrolling or function inlining when you specify -O2. As compared to -O, this option increases both compilation time and the performance of the generated code. -O2 turns on all optional optimizations except for loop unrolling, function inlining, and register renam ing. It also turns on the -fforce-mem option on all machines and frame pointer elimination on machines where doing so does not interfere with debugging. -O3 Optimize yet more. -O3 turns on all optimizations specified by -O2 and also turns on the -finline-func tions and -frename-registers options. -O0 Do not optimize. -Os Optimize for size. -Os enables all -O2 optimizations that do not typically increase code size. It also performs further optimizations designed to reduce code size. ... |
On retiendra donc les optimisations -O0, -01, -02, -03 et -0s. Comme beaucoup de bidouilleur on constaté que gcc ne refuse pas les options d'optimisation plus dure (-04, -05 et plus), nous allons les inclures pour mieux voir que cela n'apporte rien de plus à -03. (remarque ajoutée après : hormis un contexte très précis, ceci n'apporte rien)
Une dernière chose avant de passer à l'expérience en elle-même. Ces types de benchmarck dépendent beaucoup des caractéristiques de la machine, donc voici les caractéristiques de ma machine :
Ready cat /proc/cpuinfo processor : 0 vendor_id : AuthenticAMD cpu family : 6 model : 4 model name : AMD Athlon(tm) Processor stepping : 2 cpu MHz : 849.625 cache size : 256 KB fdiv_bug : no hlt_bug : no f00f_bug : no coma_bug : no fpu : yes fpu_exception : yes cpuid level : 1 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 sep mtrr pge mca cmov pat pse36 mmx fxsr syscall mmxext 3dnowext 3dnow bogomips : 1690.82 Ready |
Il ne nous reste plus qu'à travailler et à essayer toutes les combinaisons :
Ready gcc -O0 mandelbrot.c -o mandelbrot Ready time ./mandelbrot 43.84user 0.10system 0:46.24elapsed 95%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready gcc -O1 mandelbrot.c -o mandelbrot Ready time ./mandelbrot 24.13user 0.05system 0:25.39elapsed 95%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready gcc -O2 mandelbrot.c -o mandelbrot Ready time ./mandelbrot 22.38user 0.04system 0:23.52elapsed 95%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready gcc -O3 mandelbrot.c -o mandelbrot Ready time ./mandelbrot 22.59user 0.04system 0:23.97elapsed 94%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready gcc -O4 mandelbrot.c -o mandelbrot Ready time ./mandelbrot 22.59user 0.06system 0:24.25elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready gcc -O5 mandelbrot.c -o mandelbrot Ready time ./mandelbrot 22.60user 0.05system 0:24.88elapsed 93%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready gcc -Os mandelbrot.c -o mandelbrot Ready time ./mandelbrot 21.81user 0.07system 0:22.98elapsed 95%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (78major+781minor)pagefaults 0swaps Ready |
Je vous l'accorde comme cela, c'est pas pas très lisible. Si je vous ai tout mis c'est surtout dans un but pédagogique. Allez, je remet tout dans un tableau :
option | temps en seconde |
---|---|
-00 | 43,94 |
-01 | 24,18 |
-02 | 22,42 |
-03 | 22,63 |
-04 | 22,65 |
-05 | 22,65 |
-0s | 21,88 |
J'ai rapidement trois remarques à faire :
Pour la première remarque, je vous l'ai déjà expliqué, on a beaucoup de mal à reproduire deux fois la même chose sur la machine moderne.
Alors pour mieux vous convaincre, que -O3, -O4, -05, je vais utiliser la commande
Ready man cmp CMP(1) Manuel de l'utilisateur Linux CMP(1) NOM cmp - Comparer deux fichiers. SYNOPSIS cmp [-l|-s] fichier1 fichier2 [décal1[décal2]] DESCRIPTION L'utilitaire cmp compare deux fichiers de tous types et écrit le résultat sur la sortie standard. Par défaut, cmp est silencieux si les fichiers sont identiques; s'ils diffèrent, les numéros d'octet et de ligne de la première différence détectée sont affichés. ... |
Et je vais vous montrer que les binaires sont les mêmes :
Ready ll total 16 -rw------- 1 manu users 938 oct 23 22:29 makefile -rw------- 1 manu users 75 oct 23 22:08 makefile~ -rw------- 1 manu users 1394 oct 23 23:01 mandelbrot.c -rw------- 1 manu users 441 oct 23 22:06 mandelbrot.c~ Ready gcc -O3 mandelbrot.c -o mandelbrot_O3 Ready gcc -O4 mandelbrot.c -o mandelbrot_O4 Ready gcc -O5 mandelbrot.c -o mandelbrot_O5 Ready ll total 64 -rw------- 1 manu users 938 oct 23 22:29 makefile -rw------- 1 manu users 75 oct 23 22:08 makefile~ -rw------- 1 manu users 1394 oct 23 23:01 mandelbrot.c -rw------- 1 manu users 441 oct 23 22:06 mandelbrot.c~ -rwx------ 1 manu users 14181 oct 24 22:08 mandelbrot_O3* -rwx------ 1 manu users 14181 oct 24 22:08 mandelbrot_O4* -rwx------ 1 manu users 14181 oct 24 22:08 mandelbrot_O5* Ready cmp mandelbrot_O3 mandelbrot_O4 Ready cmp mandelbrot_O3 mandelbrot_O5 Ready Ready ll total 8 -rw------- 1 manu users 938 oct 23 22:29 makefile -rw------- 1 manu users 1630 oct 25 20:19 mandelbrot.c Ready gcc -O3 mandelbrot.c -o mandelbrot_O3 Ready gcc -O4 mandelbrot.c -o mandelbrot_O4 Ready gcc -O5 mandelbrot.c -o mandelbrot_O5 Ready ll total 56 -rw------- 1 manu users 938 oct 23 22:29 makefile -rw------- 1 manu users 1630 oct 25 20:19 mandelbrot.c -rwx------ 1 manu users 14345 oct 25 20:48 mandelbrot_O3* -rwx------ 1 manu users 14345 oct 25 20:48 mandelbrot_O4* -rwx------ 1 manu users 14345 oct 25 20:49 mandelbrot_O5* Ready cmp mandelbrot_O3 mandelbrot_O4 Ready cmp mandelbrot_O3 mandelbrot_O5 Ready |
Bon c'est claire, on oublie maintenant les options -O4 et -O5. (remarque ajouter après : je ne me suis pas mis dans le contexte ou ces options apportent quelques choses.)
L'autre qui m'intrigue plus, c'est que le meilleur résultat est obtenu avec -Os. En gros cela voudrait dire qu'en optimisant la taille du code, il tient entierement dans les différents caches et le processeur arrive à travailler plus vite.
Je ne sait pas comment vérifier cela sérieusement, mais en tout cas je vais jetter un coup d'oeil au code générer. En effet si on s'appuye sur le man de gcc, on apprend que l'on peut obtenir le code assembleur générer :
Ready man gcc ... -S Stop after the stage of compilation proper; do not assemble. The output is in the form of an assembler code file for each non-assembler input file specified. ... |
Et si on l'assaye sur notre programme :
Ready ll total 56 -rw------- 1 manu users 938 oct 23 22:29 makefile -rw------- 1 manu users 1630 oct 25 20:19 mandelbrot.c Ready gcc -O0 -S mandelbrot.c -o mandelbrot_O0.s Ready gcc -O0 -S mandelbrot.c -o mandelbrot_O0.s Ready gcc -O1 -S mandelbrot.c -o mandelbrot_O1.s Ready gcc -O2 -S mandelbrot.c -o mandelbrot_O2.s Ready gcc -O3 -S mandelbrot.c -o mandelbrot_O3.s Ready gcc -Os -S mandelbrot.c -o mandelbrot_Os.s Ready ll total 32 -rw------- 1 manu users 938 oct 23 22:29 makefile -rw------- 1 manu users 1630 oct 25 20:19 mandelbrot.c -rw------- 1 manu users 3338 oct 25 20:51 mandelbrot_O0.s -rw------- 1 manu users 2669 oct 25 20:51 mandelbrot_O1.s -rw------- 1 manu users 3063 oct 25 20:51 mandelbrot_O2.s -rw------- 1 manu users 4810 oct 25 20:51 mandelbrot_O3.s -rw------- 1 manu users 2535 oct 25 20:51 mandelbrot_Os.s Ready |
et si on met le tout en forme :
mandelbrot_O0.s | mandelbrot_O1.s | mandelbrot_O2.s | mandelbrot_O3.s | mandelbrot_Os.s |
---|---|---|---|---|
.file "mandelbrot.c" .section .rodata .align 8 .LC0: .long 0x0,0x40100000 .text .align 16 .globl mandelpoint .type mandelpoint,@function mandelpoint: pushl %ebp movl %esp, %ebp subl $56, %esp movl 8(%ebp), %eax movl 12(%ebp), %edx movl %eax, -8(%ebp) movl %edx, -4(%ebp) movl 16(%ebp), %eax movl 20(%ebp), %edx movl %eax, -16(%ebp) movl %edx, -12(%ebp) movl $0, -20(%ebp) movl -8(%ebp), %eax movl -4(%ebp), %edx movl %eax, -32(%ebp) movl %edx, -28(%ebp) movl -16(%ebp), %eax movl -12(%ebp), %edx movl %eax, -40(%ebp) movl %edx, -36(%ebp) fldl -32(%ebp) fmull -32(%ebp) fstpl -48(%ebp) fldl -40(%ebp) fmull -40(%ebp) fstpl -56(%ebp) .p2align 4 .L2: movl -20(%ebp), %eax cmpl 24(%ebp), %eax jge .L3 fldl -48(%ebp) faddl -56(%ebp) fldl .LC0 fucompp fnstsw %ax sahf ja .L4 jmp .L3 .p2align 4,,7 .L4: fldl -32(%ebp) fadd %st(0), %st fmull -40(%ebp) faddl -16(%ebp) fstpl -40(%ebp) fldl -48(%ebp) fsubl -56(%ebp) faddl -8(%ebp) fstpl -32(%ebp) fldl -32(%ebp) fmull -32(%ebp) fstpl -48(%ebp) fldl -40(%ebp) fmull -40(%ebp) fstpl -56(%ebp) leal -20(%ebp), %eax incl (%eax) jmp .L2 .p2align 4,,7 .L3: movl -20(%ebp), %eax movl %ebp, %esp popl %ebp ret .Lfe1: .size mandelpoint,.Lfe1-mandelpoint .align 16 .globl mandelbrot .type mandelbrot,@function mandelbrot: pushl %ebp movl %esp, %ebp subl $56, %esp movl 8(%ebp), %eax movl 12(%ebp), %edx movl %eax, -8(%ebp) movl %edx, -4(%ebp) movl 16(%ebp), %eax movl 20(%ebp), %edx movl %eax, -16(%ebp) movl %edx, -12(%ebp) movl 24(%ebp), %eax movl 28(%ebp), %edx movl %eax, -24(%ebp) movl %edx, -20(%ebp) movl 32(%ebp), %eax movl 36(%ebp), %edx movl %eax, -32(%ebp) movl %edx, -28(%ebp) movl $0, -36(%ebp) .p2align 4 .L7: movl -36(%ebp), %eax cmpl 40(%ebp), %eax jl .L10 jmp .L8 .p2align 4,,7 .L10: movl $0, -40(%ebp) .p2align 4 .L11: movl -40(%ebp), %eax cmpl 44(%ebp), %eax jl .L14 leal -36(%ebp), %eax incl (%eax) jmp .L7 .p2align 4,,7 .L14: fldl -16(%ebp) fsubl -8(%ebp) fildl -36(%ebp) fmulp %st, %st(1) fildl 40(%ebp) fdivrp %st, %st(1) fldl -8(%ebp) faddp %st, %st(1) fstpl -48(%ebp) fldl -32(%ebp) fsubl -24(%ebp) fildl -40(%ebp) fmulp %st, %st(1) fildl 44(%ebp) fdivrp %st, %st(1) fldl -24(%ebp) faddp %st, %st(1) fstpl -56(%ebp) subl $12, %esp pushl 48(%ebp) pushl -52(%ebp) pushl -56(%ebp) pushl -44(%ebp) pushl -48(%ebp) call mandelpoint addl $32, %esp movl %eax, %ecx movl -40(%ebp), %eax imull 40(%ebp), %eax addl -36(%ebp), %eax leal 0(,%eax,4), %edx movl 52(%ebp), %eax movl %ecx, (%eax,%edx) leal -40(%ebp), %eax incl (%eax) jmp .L11 .p2align 4,,7 .L8: movl %ebp, %esp popl %ebp ret .Lfe2: .size mandelbrot,.Lfe2-mandelbrot .align 16 .globl main .type main,@function main: pushl %ebp movl %esp, %ebp subl $3145768, %esp andl $-16, %esp movl $0, -12(%ebp) .p2align 4 .L16: cmpl $9, -12(%ebp) jle .L19 jmp .L17 .p2align 4,,7 .L19: leal -3145752(%ebp), %eax pushl %eax pushl $1024 pushl $768 pushl $1024 pushl $1073217536 pushl $0 pushl $-1074266112 pushl $0 pushl $1073217536 pushl $0 pushl $-1073479680 pushl $0 call mandelbrot addl $48, %esp leal -12(%ebp), %eax incl (%eax) jmp .L16 .p2align 4,,7 .L17: movl $0, %eax movl %ebp, %esp popl %ebp ret .Lfe3: .size main,.Lfe3-main .ident "GCC: (GNU) 3.0.1" |
.file "mandelbrot.c" .section .rodata .align 8 .LC0: .long 0x0,0x40100000 .text .align 16 .globl mandelpoint .type mandelpoint,@function mandelpoint: pushl %ebp movl %esp, %ebp subl $8, %esp fldl 8(%ebp) fldl 16(%ebp) movl 24(%ebp), %edx movl $0, %ecx fld %st(1) fld %st(1) fld %st(1) fmul %st(2), %st fld %st(1) fmul %st(2), %st cmpl %edx, %ecx jge .L9 fld %st(1) fadd %st(1), %st fldl .LC0 fstl -8(%ebp) fucompp fnstsw %ax sahf jbe .L8 jmp .L4 .L10: fxch %st(2) fxch %st(3) fxch %st(2) .p2align 4 .L4: fld %st(3) faddp %st, %st(4) fxch %st(3) fmulp %st, %st(2) fxch %st(1) fadd %st(3), %st fxch %st(1) fsubp %st, %st(2) fxch %st(1) fadd %st(3), %st fld %st(0) fmul %st(1), %st fld %st(2) fmul %st(3), %st incl %ecx cmpl %edx, %ecx jge .L9 fld %st(1) fadd %st(1), %st fldl -8(%ebp) fucompp fnstsw %ax sahf ja .L10 .L8: .L9: fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) movl %ecx, %eax movl %ebp, %esp popl %ebp ret .Lfe1: .size mandelpoint,.Lfe1-mandelpoint .align 16 .globl mandelbrot .type mandelbrot,@function mandelbrot: pushl %ebp movl %esp, %ebp pushl %edi pushl %esi pushl %ebx subl $28, %esp movl $0, -20(%ebp) jmp .L22 .p2align 4,,7 .L15: movl $0, %ebx cmpl 44(%ebp), %ebx jge .L14 fldl 16(%ebp) fsubl 8(%ebp) fildl -20(%ebp) fmulp %st, %st(1) fildl 40(%ebp) fdivrp %st, %st(1) faddl 8(%ebp) fstpl -32(%ebp) movl -32(%ebp), %esi movl -28(%ebp), %edi .p2align 4 .L19: fldl 32(%ebp) fsubl 24(%ebp) pushl %ebx fildl (%esp) leal -8(%esp), %esp fmulp %st, %st(1) fildl 44(%ebp) fdivrp %st, %st(1) faddl 24(%ebp) pushl 48(%ebp) leal -8(%esp), %esp fstpl (%esp) pushl %edi pushl %esi call mandelpoint movl 40(%ebp), %edx imull %ebx, %edx addl -20(%ebp), %edx movl 52(%ebp), %ecx movl %eax, (%ecx,%edx,4) addl $32, %esp incl %ebx cmpl 44(%ebp), %ebx jl .L19 .L14: incl -20(%ebp) .L22: movl 40(%ebp), %eax cmpl %eax, -20(%ebp) jl .L15 leal -12(%ebp), %esp popl %ebx popl %esi popl %edi popl %ebp ret .Lfe2: .size mandelbrot,.Lfe2-mandelbrot .align 16 .globl main .type main,@function main: pushl %ebp movl %esp, %ebp pushl %esi pushl %ebx subl $3145728, %esp andl $-16, %esp movl $0, %ebx leal -3145736(%ebp), %esi .p2align 4 .L27: pushl %esi pushl $1024 pushl $768 pushl $1024 pushl $1073217536 pushl $0 pushl $-1074266112 pushl $0 pushl $1073217536 pushl $0 pushl $-1073479680 pushl $0 call mandelbrot addl $48, %esp incl %ebx cmpl $9, %ebx jle .L27 movl $0, %eax leal -8(%ebp), %esp popl %ebx popl %esi popl %ebp ret .Lfe3: .size main,.Lfe3-main .ident "GCC: (GNU) 3.0.1" |
.file "mandelbrot.c" .section .rodata .align 8 .LC0: .long 0x0,0x40100000 .text .align 16 .globl mandelpoint .type mandelpoint,@function mandelpoint: pushl %ebp movl %esp, %ebp movl 24(%ebp), %edx fldl 8(%ebp) xorl %ecx, %ecx fldl 16(%ebp) cmpl %edx, %ecx fld %st(1) fld %st(1) fld %st(1) fld %st(1) fxch %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L10 fldl .LC0 jmp .L7 .p2align 4,,7 .L4: fxch %st(4) fadd %st(0), %st incl %ecx cmpl %edx, %ecx fmulp %st, %st(3) fsubrp %st, %st(1) fxch %st(1) fadd %st(3), %st fxch %st(1) fadd %st(4), %st fld %st(1) fld %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L9 fxch %st(1) fxch %st(2) fxch %st(4) .L7: fld %st(2) fadd %st(2), %st fxch %st(1) fucom %st(1) fnstsw %ax fstp %st(1) sahf ja .L4 .L9: fstp %st(0) .L10: fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) popl %ebp movl %ecx, %eax ret .Lfe1: .size mandelpoint,.Lfe1-mandelpoint .align 16 .globl mandelbrot .type mandelbrot,@function mandelbrot: pushl %ebp movl %esp, %ebp pushl %edi xorl %edi, %edi pushl %esi pushl %ebx subl $92, %esp cmpl 40(%ebp), %edi fldl 8(%ebp) fldl 24(%ebp) jge .L24 .p2align 4 .L15: xorl %esi, %esi cmpl 44(%ebp), %esi jge .L23 pushl %edi movl 52(%ebp), %eax fildl (%esp) addl $4, %esp leal (%eax,%edi,4), %ebx fldl 16(%ebp) fildl 40(%ebp) fldl 32(%ebp) fxch %st(2) fsub %st(5), %st fildl 44(%ebp) fxch %st(3) fsub %st(5), %st fxch %st(1) fmulp %st, %st(4) fxch %st(3) fdivp %st, %st(1) fadd %st(4), %st jmp .L19 .L25: fxch %st(4) fxch %st(1) fxch %st(3) fxch %st(2) fxch %st(1) .p2align 4 .L19: pushl %esi movl 48(%ebp), %eax incl %esi fildl (%esp) fxch %st(1) subl $8, %esp pushl %eax subl $16, %esp fstl (%esp) fstpt -40(%ebp) fxch %st(2) fld %st(0) fstpt -56(%ebp) fxch %st(1) fld %st(0) fstpt -72(%ebp) fxch %st(2) fmulp %st, %st(1) fxch %st(2) fld %st(0) fstpt -88(%ebp) fxch %st(3) fstpt -104(%ebp) fdivrp %st, %st(1) faddp %st, %st(1) fstpl 8(%esp) call mandelpoint movl %eax, (%ebx) addl $32, %esp movl 40(%ebp), %eax cmpl 44(%ebp), %esi fldt -40(%ebp) leal (%ebx,%eax,4), %ebx fldt -56(%ebp) fldt -72(%ebp) fldt -88(%ebp) fldt -104(%ebp) jl .L25 fstp %st(2) fstp %st(2) fstp %st(2) .L23: incl %edi cmpl 40(%ebp), %edi jl .L15 .L24: fstp %st(0) fstp %st(0) leal -12(%ebp), %esp popl %ebx popl %esi popl %edi popl %ebp ret .Lfe2: .size mandelbrot,.Lfe2-mandelbrot .align 16 .globl main .type main,@function main: pushl %ebp movl %esp, %ebp pushl %esi leal -3145736(%ebp), %esi pushl %ebx movl $9, %ebx subl $3145728, %esp andl $-16, %esp .p2align 4 .L30: pushl %esi pushl $1024 pushl $768 pushl $1024 pushl $1073217536 pushl $0 pushl $-1074266112 pushl $0 pushl $1073217536 pushl $0 pushl $-1073479680 pushl $0 call mandelbrot addl $48, %esp decl %ebx jns .L30 leal -8(%ebp), %esp xorl %eax, %eax popl %ebx popl %esi popl %ebp ret .Lfe3: .size main,.Lfe3-main .ident "GCC: (GNU) 3.0.1" |
.file "mandelbrot.c" .section .rodata .align 8 .LC2: .long 0x0,0xc0040000 .align 8 .LC4: .long 0x0,0xbff80000 .align 8 .LC5: .long 0x0,0x40100000 .align 8 .LC6: .long 0x0,0x40900000 .align 8 .LC7: .long 0x0,0x40880000 .align 8 .LC8: .long 0x0,0x40080000 .text .align 16 .globl main .type main,@function main: pushl %ebp xorl %edx, %edx movl %esp, %ebp pushl %edi movl $1074790400, %ecx leal -3145752(%ebp), %edi pushl %esi xorl %esi, %esi pushl %ebx subl $3145756, %esp movl %ecx, -3145756(%ebp) andl $-16, %esp movl %edx, -3145760(%ebp) fldl -3145760(%ebp) .p2align 4 .L28: xorl %ebx, %ebx .p2align 4 .L31: pushl %ebx xorl %ecx, %ecx leal (%edi,%ebx,4), %edx fildl (%esp) addl $4, %esp fmull .LC5 fdivl .LC6 faddl .LC2 .p2align 4 .L34: pushl %ecx fld %st(0) fld %st(0) fildl (%esp) fxch %st(1) xorl %eax, %eax fmul %st(0), %st movl %eax, -3145764(%ebp) addl $4, %esp fld %st(0) fxch %st(2) fmull .LC8 fdivl .LC7 faddl .LC4 fld %st(0) fld %st(0) fmul %st(0), %st jmp .L50 .p2align 4,,7 .L37: fxch %st(4) incl -3145764(%ebp) fadd %st(0), %st cmpl $1024, -3145764(%ebp) fmulp %st, %st(4) fxch %st(1) fsubp %st, %st(5) fadd %st, %st(2) fxch %st(4) fadd %st(3), %st fld %st(2) fld %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L51 fld %st(1) fxch %st(4) fxch %st(7) fxch %st(2) fxch %st(3) fxch %st(5) fxch %st(1) .L50: fadd %st, %st(4) fxch %st(7) fucom %st(4) fnstsw %ax fstp %st(4) sahf ja .L37 .L51: fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(1) fstp %st(2) movl -3145764(%ebp), %eax incl %ecx movl %eax, (%edx) addl $4096, %edx cmpl $768, %ecx jl .L34 fstp %st(0) incl %ebx cmpl $1024, %ebx jl .L31 incl %esi cmpl $9, %esi jle .L28 fstp %st(0) leal -12(%ebp), %esp xorl %eax, %eax popl %ebx popl %esi popl %edi popl %ebp ret .Lfe1: .size main,.Lfe1-main .section .rodata .align 8 .LC0: .long 0x0,0x40100000 .text .align 16 .globl mandelpoint .type mandelpoint,@function mandelpoint: pushl %ebp movl %esp, %ebp movl 24(%ebp), %edx fldl 8(%ebp) xorl %ecx, %ecx fldl 16(%ebp) cmpl %edx, %ecx fld %st(1) fld %st(1) fld %st(1) fld %st(1) fxch %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L55 fldl .LC0 jmp .L52 .p2align 4,,7 .L4: fxch %st(4) fadd %st(0), %st incl %ecx cmpl %edx, %ecx fmulp %st, %st(3) fsubrp %st, %st(1) fxch %st(1) fadd %st(3), %st fxch %st(1) fadd %st(4), %st fld %st(1) fld %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L54 fxch %st(1) fxch %st(2) fxch %st(4) .L52: fld %st(2) fadd %st(2), %st fxch %st(1) fucom %st(1) fnstsw %ax fstp %st(1) sahf ja .L4 .L54: fstp %st(0) .L55: fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) popl %ebp movl %ecx, %eax ret .Lfe2: .size mandelpoint,.Lfe2-mandelpoint .align 16 .globl mandelbrot .type mandelbrot,@function mandelbrot: pushl %ebp movl %esp, %ebp pushl %edi pushl %esi pushl %ebx xorl %ebx, %ebx subl $36, %esp cmpl 40(%ebp), %ebx jge .L56 .p2align 4 .L11: xorl %ecx, %ecx cmpl 44(%ebp), %ecx jge .L57 pushl %ebx movl 52(%ebp), %eax fildl (%esp) addl $4, %esp leal (%eax,%ebx,4), %edx fldl 16(%ebp) fildl 40(%ebp) fxch %st(1) movl 40(%ebp), %eax fsubl 8(%ebp) fildl 44(%ebp) sall $2, %eax fldl 32(%ebp) fxch %st(2) fmulp %st, %st(4) fxch %st(1) movl %eax, -48(%ebp) fsubl 24(%ebp) fxch %st(3) fdivp %st, %st(2) fxch %st(1) faddl 8(%ebp) fxch %st(1) fstpl -32(%ebp) fxch %st(1) fstpl -24(%ebp) .p2align 4 .L15: pushl %ecx movl 48(%ebp), %eax fld %st(0) fildl (%esp) movl $0, -44(%ebp) addl $4, %esp fld %st(1) cmpl %eax, -44(%ebp) fmul %st(0), %st fxch %st(1) fmull -24(%ebp) fdivl -32(%ebp) faddl 24(%ebp) fld %st(0) fld %st(0) fmul %st(0), %st jge .L61 xorl %edi, %edi movl $1074790400, %eax fld %st(3) movl %eax, -36(%ebp) fadd %st(1), %st movl %edi, -40(%ebp) fldl -40(%ebp) jmp .L58 .p2align 4,,7 .L18: fxch %st(5) incl -44(%ebp) fadd %st(0), %st movl 48(%ebp), %eax fmulp %st, %st(2) cmpl %eax, -44(%ebp) fsubrp %st, %st(3) fadd %st(1), %st fxch %st(2) fadd %st(4), %st fld %st(2) fld %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L60 fld %st(1) fadd %st(1), %st fxch %st(1) fxch %st(2) fxch %st(5) fxch %st(3) fxch %st(6) .L58: fucom %st(1) fnstsw %ax fstp %st(1) sahf ja .L18 .L60: fstp %st(0) .L61: fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) movl -44(%ebp), %eax incl %ecx movl %eax, (%edx) movl -48(%ebp), %edi addl %edi, %edx cmpl 44(%ebp), %ecx jl .L15 fstp %st(0) .L57: incl %ebx cmpl 40(%ebp), %ebx jl .L11 .L56: addl $36, %esp popl %ebx popl %esi popl %edi popl %ebp ret .Lfe3: .size mandelbrot,.Lfe3-mandelbrot .ident "GCC: (GNU) 3.0.1" |
.file "mandelbrot.c" .section .rodata .align 8 .LC0: .long 0x0,0x40100000 .text .align 16 .globl mandelpoint .type mandelpoint,@function mandelpoint: pushl %ebp movl %esp, %ebp movl 24(%ebp), %edx fldl 8(%ebp) xorl %ecx, %ecx fldl 16(%ebp) cmpl %edx, %ecx fld %st(1) fld %st(1) fld %st(1) fld %st(1) fxch %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L10 fldl .LC0 jmp .L7 .p2align 4,,7 .L4: fxch %st(4) fadd %st(0), %st incl %ecx cmpl %edx, %ecx fmulp %st, %st(3) fsubrp %st, %st(1) fxch %st(1) fadd %st(3), %st fxch %st(1) fadd %st(4), %st fld %st(1) fld %st(1) fmul %st(0), %st fxch %st(1) fmul %st(0), %st jge .L9 fxch %st(1) fxch %st(2) fxch %st(4) .L7: fld %st(2) fadd %st(2), %st fxch %st(1) fucom %st(1) fnstsw %ax fstp %st(1) sahf ja .L4 .L9: fstp %st(0) .L10: fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) fstp %st(0) popl %ebp movl %ecx, %eax ret .Lfe1: .size mandelpoint,.Lfe1-mandelpoint .align 16 .globl mandelbrot .type mandelbrot,@function mandelbrot: pushl %ebp movl %esp, %ebp pushl %edi xorl %edi, %edi pushl %esi pushl %ebx subl $12, %esp cmpl 40(%ebp), %edi jge .L22 .p2align 4 .L15: xorl %esi, %esi cmpl 44(%ebp), %esi jge .L23 movl 52(%ebp), %eax leal (%eax,%edi,4), %ebx .p2align 4 .L19: pushl %edi fildl (%esp) fldl 16(%ebp) movl %esi, (%esp) incl %esi fildl (%esp) fxch %st(1) subl $8, %esp fsubl 8(%ebp) pushl 48(%ebp) fmulp %st, %st(2) subl $16, %esp fildl 40(%ebp) fdivrp %st, %st(2) fldl 32(%ebp) fxch %st(2) faddl 8(%ebp) fxch %st(2) fsubl 24(%ebp) fxch %st(2) fstpl (%esp) fmulp %st, %st(1) fildl 44(%ebp) fdivrp %st, %st(1) faddl 24(%ebp) fstpl 8(%esp) call mandelpoint movl %eax, (%ebx) addl $32, %esp movl 40(%ebp), %eax cmpl 44(%ebp), %esi leal (%ebx,%eax,4), %ebx jl .L19 .L23: incl %edi cmpl 40(%ebp), %edi jl .L15 .L22: leal -12(%ebp), %esp popl %ebx popl %esi popl %edi popl %ebp ret .Lfe2: .size mandelbrot,.Lfe2-mandelbrot .align 16 .globl main .type main,@function main: pushl %ebp movl %esp, %ebp pushl %ebx subl $3145732, %esp andl $-16, %esp movl $9, %ebx .p2align 4 .L28: leal -3145736(%ebp), %eax pushl %eax pushl $1024 pushl $768 pushl $1024 pushl $1073217536 pushl $0 pushl $-1074266112 pushl $0 pushl $1073217536 pushl $0 pushl $-1073479680 pushl $0 call mandelbrot addl $48, %esp decl %ebx jns .L28 movl -4(%ebp), %ebx xorl %eax, %eax leave ret .Lfe3: .size main,.Lfe3-main .ident "GCC: (GNU) 3.0.1" |
Bon, je préviens tout de suite mes connaissances en assembleurs sont assez limitées, donc soyez indulgeant en cas d'erreur.
Une première remarque sur le code sans optimisation, on constate que les variables sont systématiquement chargées en mémoire, ce qui explique les temps assez long. (Sur certaine architecture avec des caches très performant, on arrive à supprimer cette effet)
L'optimisation O1 utilise un peu mieux les registres, donc moins de travail avec la mémoire, d'ou gain de vitesse.
Arrivé à O2, on voit que le comilateur utilise mieux les instructions assembleurs, d'ou encore un petit gain.
Pour la fonction mandelpoint, le code est le même pour les options
O2, O3, Os. Il est claire que le programme passe la majorité du temps
dans cette fonction. (un autre jour je vous expliquerai comment le voir
avec
En gros on constate que pour O3, le compilateur a "inliné" la fonction mandelpoint et il a modifié l'ordre des fonctions, alors que pour Os, il a simplement mis un code plus petit.
Je n'ai pas d'explication sur pourquoi "inliné" la fonction fait perdre du temps.
Par contre sur pourquoi Os sort gagnant, je pense que l'explication tient du faite que tout le programme tient dans un cache ce qui accèlère énormement l'execution.
Bergeron Etienne m'a confirmé que cette différence de vitesse vient bien du faite que le programme plus petit tient entièrement dans le cache donc s'execute plus rapidement.
C'est tout pour ce soir, suite au prochain numéro !