Les deux boucles sont infinies, mais nous pouvons voir que l'on prend plus d'instructions/ressources par itération.
À l'aide de gcc, j'ai compilé les deux programmes suivants à l'assemblée, à divers niveaux d'optimisation:
int main(void)
{
while(1)
{
}
return 0;
}
int main(void)
{
while(2)
{
}
return 0;
}
Même sans les optimisations (-O0
), l'assembly généré est identique pour les deux programmes. Par conséquent, il n'y a pas de différence de vitesse entre les deux boucles.
Pour référence, voici l'assembly généré (à l'aide d' gcc main.c -S -masm=intel
avec une option d'optimisation):
Avec -O0
:
.file "main.c"
.intel_syntax noprefix
.def __main; .scl 2; .type 32; .endef
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
push rbp
.seh_pushreg rbp
mov rbp, rsp
.seh_setframe rbp, 0
sub rsp, 32
.seh_stackalloc 32
.seh_endprologue
call __main
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
Avec -O1
:
.file "main.c"
.intel_syntax noprefix
.def __main; .scl 2; .type 32; .endef
.text
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
call __main
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
Avec -O2
et -O3
(même résultat):
.file "main.c"
.intel_syntax noprefix
.def __main; .scl 2; .type 32; .endef
.section .text.startup,"x"
.p2align 4,,15
.globl main
.def main; .scl 2; .type 32; .endef
.seh_proc main
main:
sub rsp, 40
.seh_stackalloc 40
.seh_endprologue
call __main
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
En fait, l'assembly généré par la boucle est identique pour chaque sortie:
.L2:
jmp .L2
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
Je ne peux pas lire l'assemblée très bien, mais c'est évidemment une inconditionnelle de la boucle. L' jmp
instruction inconditionnellement réinitialise le programme de retour à l' .L2
label sans même comparer une valeur à l'encontre du vrai, et bien sûr immédiatement fait à nouveau jusqu'à ce que le programme est en quelque sorte terminé.
Edit:
Ce qui est assez intéressant, même avec aucun des optimisations, les boucles suivantes, qui sont toutes produites exactement la même sortie (inconditionnel jmp
) dans l'assemblée:
while(42)
{
}
while(1==1)
{
}
while(2==2)
{
}
while(4<7)
{
}
while(3==3 && 4==4)
{
}
while(8-9 < 0)
{
}
while(4.3 * 3e4 >= 2 << 6)
{
}
while(-0.1 + 02)
{
}
Et même à mon grand étonnement:
#include<math.h>
while(sqrt(7))
{
}
while(hypot(3,4))
{
}
Les choses deviennent un peu plus intéressantes, avec des fonctions définies par l'utilisateur:
int x(void)
{
return 1;
}
while(x())
{
}
#include<math.h>
double x(void)
{
return sqrt(7);
}
while(x())
{
}
En -O0
, ces deux exemples d'appels x
et d'effectuer une comparaison pour chaque itération.
Premier exemple (retour 1):
.L4:
call x
testl %eax, %eax
jne .L4
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
Deuxième exemple (en rentrant sqrt(7)
):
.L4:
call x
xorpd %xmm1, %xmm1
ucomisd %xmm1, %xmm0
jp .L4
xorpd %xmm1, %xmm1
ucomisd %xmm1, %xmm0
jne .L4
movl $0, %eax
addq $32, %rsp
popq %rbp
ret
.seh_endproc
.ident "GCC: (tdm64-2) 4.8.1"
Toutefois, en -O1
et au-dessus, ils produisent la même assemblée que les exemples précédents (un inconditionnel jmp
retour à la précédente étiquette).
TL;DR
Lorsque les différentes boucles sont compilés à l'assemblée, le compilateur évalue les valeurs de constante et n'a pas pris la peine d'effectuer toute comparaison; les deux boucles sont identiques.
Même si cela ne prouve pas que ce comportement est cohérent à travers tous les compilateurs/plates-formes, il s'avère que le compilateur peut optimiser ces boucles identiques, et, par conséquent, devrait. L'un des principaux avantages de l'utilisation d'un langage compilé, c'est le fait que ce genre de chose est censé être à l'extérieur de l'entreprise de programmation de préoccupation.