J'ai précisé que j'avais utilisé -O0. Avec -O2, j'obtient exactement la même chose, tu as raison.
Thierry
Version imprimable
Oui, c'est autant présent quand il n'y a pas de variable temporaire. Regarde le code de Thierry Chappuis, on a d'un côté :
Et de l'autre :Code:
1
2movl %esp, %ebp subl $8, %esp
Vu que dans tous les cas, il y a des choses empilées (adresse de retour, int en retour).Code:
1
2movl %esp, %ebp subl $16, %esp
Je ne dis pas que le raisonnement de Melem est juste, mais dans le cas où le compilateur n'utiliserait aucune variable sur la pile à part les arguments il doit réserver de la place sur la pile avec sub et à la fin les libérer avec un mov ou leave comme dans le code de Thierry. La valeur de retour est mise dans EAX.
Ben on dirait qu'on va tout reprendre dès le début. Si on a deux fonctions
Sans optimisation (compilation bête, mot pour mot, dent pour dent) on aura pour f :Code:
1
2
3
4
5
6
7
8
9
10
11 int f() { return 10; } int g() { int x = 10; return x; }
Et pour g :Code:
1
2mov eax, 10
Et je peux vous grantir qu'une opération de lecture ou écriture en mémoire c'est vachement long.Code:
1
2
3
4 ;Désignons par x l'adresse de la variable x mov DWORD PTR [x], 10 mov eax, [x]
Bonjour,
Mais qui de nos jours, génère un programme sans optimisation?
Je pense personne.
Donc les fonctions f et g sont comparables.
Dans le cas d'un langage compilé comme le C le compilateur va certainement effectuer une optimisation en modifiant g comme si on avait écrit f. C'est moins évident que cela dans le cas d'un langage interprété. Un programme écrit dans un langage interprété pour un robot par exemple, le robot il va tout simplement faire ce qu'on lui dit. Mais là encore ca dépend de la manière dont on a programmé le robot ... Mais à priori, f et g sont différentes et f est plus rapide que g
Merci pour toutes ces réponses.
Corrigez-moi si je me trompe mais il me semble avoir compris que si on veut gratter au maximum, il vaut mieux éviter de passer par des variables intermédiaires, lorsque c'est possible, afin de diminuer les accès mémoire. Je crois comprendre aussi que, dans la pratique, le code peut être optimisé par le compilateur et que ce genre de préoccupations deviennent donc secondaires (par rapport au choix de la bonne méthode algorithmique, mais je suppose cela déjà fait).
Ma dernière question est : est-ce vraiment le cas, c'est-à-dire, peut-on vraiment faire confiance au compilateur pour faire ces optimisations et comment être sûr qu'il les fait ?
Bien que je vais me faire fusiller une fois de plus en disant ceci, j'affirme quand même que plus tu utilises de variables, moins ton code sera rapide (à cause du temps d'accès à la mémoire). Dans certains cas j'ai bien peur que le compilateur ne fasse aucune optimisation, comme lorsqu'on utilise des constantes (variables déclarées avec le mot clé const) par exemple.
Une certaine optimisation qui ne va pas rendre le code plus rapide que si on utilisait directement des constantes littérales (en pratique on utilisera des macros).Citation:
Envoyé par Thierry Chappuis
Melem, on ne va pas te fusiller seulement te prendre par les pieds. ;)
On veut bien te croire mais avance-nous les preuves de ce que tu avances ;).
A méditer
avec -0 (gcc -c -g -Wa,-a,-ad -O main.c > main.out), on remarque que le code généré pour les 3 fonctions est le même.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 #include <stdlib.h> #include <stdio.h> static int f() { return 10; } static int g() { int x = 10; return x; } static int h() { int const x = 10; return x; } int main(int argc, char *argv[]) { printf("f: %d\n", f()); printf("g: %d\n", g()); printf("h: %d\n", h()); return EXIT_SUCCESS; }
avec -02 (gcc -c -g -Wa,-a,-ad -O2 main.c > main.out), on arrive à peine à reconnaitre le code CCode:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116 1 .file "main.c" 4 .text 5 Ltext0: 208 _f: 1:main.c **** #include <stdlib.h> 2:main.c **** #include <stdio.h> 3:main.c **** 4:main.c **** static int f() { 210 LM1: 211 0000 55 pushl %ebp 212 0001 89E5 movl %esp, %ebp 5:main.c **** return 10; 6:main.c **** } 214 LM2: 215 0003 B80A0000 movl $10, %eax 215 00 216 0008 5D popl %ebp 217 0009 C3 ret 218 Lscope0: 222 _g: 7:main.c **** 8:main.c **** static int g() { 224 LM3: 225 000a 55 pushl %ebp 226 000b 89E5 movl %esp, %ebp 9:main.c **** int x = 10; 10:main.c **** return x; 11:main.c **** } 228 LM4: 229 000d B80A0000 movl $10, %eax 229 00 230 0012 5D popl %ebp 231 0013 C3 ret 232 Lscope1: 236 _h: 12:main.c **** 13:main.c **** static int h() { 238 LM5: 239 0014 55 pushl %ebp 240 0015 89E5 movl %esp, %ebp 14:main.c **** int const x = 10; 15:main.c **** return x; 16:main.c **** } 242 LM6: 243 0017 B80A0000 movl $10, %eax 243 00 244 001c 5D popl %ebp 245 001d C3 ret 246 Lscope2: 249 .section .rdata,"dr" 250 LC0: 251 0000 663A2025 .ascii "f: %d\12\0" 251 640A00 252 LC1: 253 0007 673A2025 .ascii "g: %d\12\0" 253 640A00 254 LC2: 255 000e 683A2025 .ascii "h: %d\12\0" 255 640A00 256 0015 00000000 .text 256 00000000 256 000000 260 .globl _main 262 _main: 17:main.c **** 18:main.c **** int main(int argc, char *argv[]) { 264 LM7: 265 001e 55 pushl %ebp 266 001f 89E5 movl %esp, %ebp 267 0021 83EC08 subl $8, %esp 268 0024 83E4F0 andl $-16, %esp 269 0027 B8100000 movl $16, %eax 269 00 270 002c E8000000 call __alloca 270 00 272 LM8: 273 0031 E8000000 call ___main 273 00 19:main.c **** printf("f: %d\n", f()); 275 LM9: 276 0036 E8C5FFFF call _f 276 FF 277 003b 89442404 movl %eax, 4(%esp) 278 003f C7042400 movl $LC0, (%esp) 278 000000 279 0046 E8000000 call _printf 279 00 20:main.c **** printf("g: %d\n", g()); 281 LM10: 282 004b E8BAFFFF call _g 282 FF 283 0050 89442404 movl %eax, 4(%esp) 284 0054 C7042407 movl $LC1, (%esp) 284 000000 285 005b E8000000 call _printf 285 00 21:main.c **** printf("h: %d\n", h()); 287 LM11: 288 0060 E8AFFFFF call _h 288 FF 289 0065 89442404 movl %eax, 4(%esp) 290 0069 C704240E movl $LC2, (%esp) 290 000000 291 0070 E8000000 call _printf 291 00 22:main.c **** return EXIT_SUCCESS; 23:main.c **** } 293 LM12: 294 0075 B8000000 movl $0, %eax 294 00 295 007a C9 leave 296 007b C3 ret 297 Lscope3: 299 .text 301 Letext:
Le code généré sans optimisation est effectivement plus lent si on utilise des variables.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 GAS LISTING 1 .file "main.c" 4 .text 5 Ltext0: 207 .section .rdata,"dr" 208 LC0: 209 0000 663A2025 .ascii "f: %d\12\0" 209 640A00 210 LC1: 211 0007 673A2025 .ascii "g: %d\12\0" 211 640A00 212 LC2: 213 000e 683A2025 .ascii "h: %d\12\0" 213 640A00 214 0015 00000000 .text 214 00000000 214 000000 215 .p2align 4,,15 219 .globl _main 221 _main: 1:main.c **** #include <stdlib.h> 2:main.c **** #include <stdio.h> 3:main.c **** 4:main.c **** static int f() { 5:main.c **** return 10; 6:main.c **** } 7:main.c **** 8:main.c **** static int g() { 9:main.c **** int x = 10; 10:main.c **** return x; 11:main.c **** } 12:main.c **** 13:main.c **** static int h() { 14:main.c **** int const x = 10; 15:main.c **** return x; 16:main.c **** } 17:main.c **** 18:main.c **** int main(int argc, char *argv[]) { 223 LM1: 224 0000 55 pushl %ebp 225 0001 B8100000 movl $16, %eax 225 00 226 0006 89E5 movl %esp, %ebp 227 0008 83EC08 subl $8, %esp 229 LM2: 230 000b 83E4F0 andl $-16, %esp 231 000e E8000000 call __alloca 231 00 232 0013 E8000000 call ___main 232 00 234 LM3: 235 0018 C7042400 movl $LC0, (%esp) 235 000000 236 001f B90A0000 movl $10, %ecx 236 00 237 0024 894C2404 movl %ecx, 4(%esp) 238 0028 E8000000 call _printf 238 00 240 LM4: 241 002d C7042407 movl $LC1, (%esp) 241 000000 242 0034 BA0A0000 movl $10, %edx 242 00 243 0039 89542404 movl %edx, 4(%esp) 244 003d E8000000 call _printf 244 00 246 LM5: 247 0042 C704240E movl $LC2, (%esp) 247 000000 248 0049 B80A0000 movl $10, %eax 248 00 249 004e 89442404 movl %eax, 4(%esp) 250 0052 E8000000 call _printf 250 00 19:main.c **** printf("f: %d\n", f()); 20:main.c **** printf("g: %d\n", g()); 21:main.c **** printf("h: %d\n", h()); 22:main.c **** return EXIT_SUCCESS; 23:main.c **** } 252 LM6: 253 0057 C9 leave 254 0058 31C0 xorl %eax, %eax 255 005a C3 ret 256 Lscope0: 258 .text 260 Letext:
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131 1 .file "main.c" 4 .text 5 Ltext0: 208 _f: 1:main.c **** #include <stdlib.h> 2:main.c **** #include <stdio.h> 3:main.c **** 4:main.c **** static int f() { 210 LM1: 211 0000 55 pushl %ebp 212 0001 89E5 movl %esp, %ebp 5:main.c **** return 10; 214 LM2: 215 0003 B80A0000 movl $10, %eax 215 00 6:main.c **** } 217 LM3: 218 0008 5D popl %ebp 219 0009 C3 ret 220 Lscope0: 224 _g: 7:main.c **** 8:main.c **** static int g() { 226 LM4: 227 000a 55 pushl %ebp 228 000b 89E5 movl %esp, %ebp 229 000d 83EC04 subl $4, %esp 9:main.c **** int x = 10; 231 LM5: 232 0010 C745FC0A movl $10, -4(%ebp) 232 000000 10:main.c **** return x; 234 LM6: 235 0017 8B45FC movl -4(%ebp), %eax 11:main.c **** } 237 LM7: 238 001a C9 leave 239 001b C3 ret 243 Lscope1: 247 _h: 12:main.c **** 13:main.c **** static int h() { 249 LM8: 250 001c 55 pushl %ebp 251 001d 89E5 movl %esp, %ebp 252 001f 83EC04 subl $4, %esp 14:main.c **** int const x = 10; 254 LM9: 255 0022 C745FC0A movl $10, -4(%ebp) 255 000000 15:main.c **** return x; 257 LM10: 258 0029 8B45FC movl -4(%ebp), %eax 16:main.c **** } 260 LM11: 261 002c C9 leave 262 002d C3 ret 266 Lscope2: 269 .section .rdata,"dr" 270 LC0: 271 0000 663A2025 .ascii "f: %d\12\0" 271 640A00 272 LC1: 273 0007 673A2025 .ascii "g: %d\12\0" 273 640A00 274 LC2: 275 000e 683A2025 .ascii "h: %d\12\0" 275 640A00 276 0015 00000000 .text 276 00000000 276 000000 280 .globl _main 282 _main: 17:main.c **** 18:main.c **** int main(int argc, char *argv[]) { 284 LM12: 285 002e 55 pushl %ebp 286 002f 89E5 movl %esp, %ebp 287 0031 83EC18 subl $24, %esp 288 0034 83E4F0 andl $-16, %esp 289 0037 B8000000 movl $0, %eax 289 00 290 003c 83C00F addl $15, %eax 291 003f 83C00F addl $15, %eax 292 0042 C1E804 shrl $4, %eax 293 0045 C1E004 sall $4, %eax 294 0048 8945FC movl %eax, -4(%ebp) 295 004b 8B45FC movl -4(%ebp), %eax 296 004e E8000000 call __alloca 296 00 298 LM13: 299 0053 E8000000 call ___main 299 00 19:main.c **** printf("f: %d\n", f()); 301 LM14: 302 0058 E8A3FFFF call _f 302 FF 303 005d 89442404 movl %eax, 4(%esp) 304 0061 C7042400 movl $LC0, (%esp) 304 000000 305 0068 E8000000 call _printf 305 00 20:main.c **** printf("g: %d\n", g()); 307 LM15: 308 006d E898FFFF call _g 308 FF 309 0072 89442404 movl %eax, 4(%esp) 310 0076 C7042407 movl $LC1, (%esp) 310 000000 311 007d E8000000 call _printf 311 00 21:main.c **** printf("h: %d\n", h()); 313 LM16: 314 0082 E895FFFF call _h 314 FF 315 0087 89442404 movl %eax, 4(%esp) 316 008b C704240E movl $LC2, (%esp) 316 000000 317 0092 E8000000 call _printf 317 00 22:main.c **** return EXIT_SUCCESS; 319 LM17: 320 0097 B8000000 movl $0, %eax 320 00 23:main.c **** } 322 LM18: 323 009c C9 leave 324 009d C3 ret 325 Lscope3: 327 .text 329 Letext:
Suis-je bête pourquoi n'y ai-je pas pensé plutôt, merci beaucoup.Citation:
Envoyé par jowo
Code:
1
2
3
4
5
6
7 int f1() { int x; x = 10; return x; }
Code:
1
2
3
4
5
6
7
8 int f2() { int x; int const y = 10; x = y; return x; }
Version : gcc (GCC) 3.4.4 (mingw special)Code:gcc -S f.c
Listing assembleur pour f1:
Et pour f2:Code:
1
2
3
4
5
6
7
8 pushl %ebp movl %esp, %ebp subl $4, %esp movl $10, -4(%ebp) movl -4(%ebp), %eax leave ret
Code:
1
2
3
4
5
6
7
8
9
10 pushl %ebp movl %esp, %ebp subl $8, %esp movl $10, -8(%ebp) movl -8(%ebp), %eax movl %eax, -4(%ebp) movl -4(%ebp), %eax leave ret
Bien sûr que sans optimisations (ou avec un niveau d'optimisation faible), il y a des différences. Personne n'a jamais dit le contraire. Compilé avec gcc 4.1.3 et l'option -O2, j'obtiens:
Code:
1
2
3
4
5
6
7f1: pushl %ebp movl $10, %eax movl %esp, %ebp popl %ebp ret
Si ça t'amuse d'écrire du code esotérique et de passer tes journées à micro-optimiser ton code et économiser une ou deux variables... Je fais confiance dans la plupart des cas aux capacités d'optimisation de mon compilo, et tout ce que tu as montré jusqu'ici me conforte dans cette idée.Code:
1
2
3
4
5
6f2: pushl %ebp movl $10, %eax movl %esp, %ebp popl %ebp ret
Thierry
L'exemple ici est trop facile et le compilateur n'a pas eu de mal à modifier f2 en f1, mais dans un exemple plus complexe ce serait différent. Ce qu'il faut retenir de mon exemple c'est que :etCode:x = 10;
, dans le cas général, ou y est une variable (ou une constante) valant 10, ne sont pas les mêmes. Dans le cas des processeurs x86 par exemple, Il n'y a pas d'instruction de transfert de mémoire à mémoire, on passe tout d'abord par un registre ce qui nécessite 2 instructions. Par contre on peut placer une valeur immédiate dans la mémoire, donc une instruction seulement.Code:x = y