Articles tagués “perfomance

Boucle infinie en C : for ou while ?

On s’est déjà posé cette question, on a déjà vu ou entendu des gens la poser : faut-il faire une boucle infinie avec while (1) ou avec for( ; ; ) en C ?

La logique veut que le résultat soit le même. J’ai donc fait le test avec le compilateur MinGW sous Windows 7 64 bits. J’ai pour cela écrit deux fonctions placées dans le fichier boucles.c :

void withFor(void)
{
    for(;;)
        ;
}

void withWhile(void)
{
    while(1)
        ;
}

J’ai ensuite utilisé objdump pour déassembler le code :

PS D:\Users\pgradot\Documents\C\out\Debug> objdump.exe -d .\boucles.o
.\boucles.o:     file format pe-i386

Disassembly of section .text:

00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   eb fe                   jmp    3 

00000005 :
   5:   55                      push   %ebp
   6:   89 e5                   mov    %esp,%ebp
   8:   eb fe                   jmp    8 
   a:   90                      nop
   b:   90                      nop

Les 2 NOP à la fin m’ont étonné. J’ai inversé les positions des fonctions dans le fichier et j’ai alors constaté que les 2 NOP étaient toujours à la fin du fichier déassemblé. Pour m’assurer qu’ils ne faisaient effectivement pas partie des fonctions (vu les JMP, c’était quasiment certain), j’ai rajouté une fonction à la fin du fichier :

void withWhile(void)
{
    while(1)
        ;
}

void withFor(void)
{
    for(;;)
        ;
}

void nop()
{

}

Voici le résultat en assembleur :

PS D:\Users\pgradot\Documents\C\out\Debug> objdump.exe -d .\boucles.o

.\boucles.o:     file format pe-i386

Disassembly of section .text:

00000000 :
   0:   55                      push   %ebp
   1:   89 e5                   mov    %esp,%ebp
   3:   eb fe                   jmp    3 

00000005 :
   5:   55                      push   %ebp
   6:   89 e5                   mov    %esp,%ebp
   8:   eb fe                   jmp    8 

0000000a :
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   5d                      pop    %ebp
   e:   c3                      ret
   f:   90                      nop

Il n’y a donc aucune différence de performance entre les deux boucles, en tout cas avec ce compilateur et cette cible. J’ai des résultats similaires avec llvm/gcc et otool sous Mac OS X. Il en y a qui disent que for( ; ; ) donnerait de meilleures performances que while (1) car il y a une évaluation de condition dans la deuxième écriture. A l’évidence, mon compilateur ne se laisse pas avoir. Ce n’était peut-être pas le cas avec des compilateurs anciens mais je pense qu’on peut dire que cela relève maintenant des mythes du passé.

Il peut enfin rester des considérations sémantiques : laquelle des deux écritures représentent mieux l’idée de boucle infinie ? Je pense que cette discussion de stackoverflow les résume bien. En particulier, j’aime beaucoup l’écriture suivante qui y est proposée et donc la sémantique est parfaite ^^ :

#define EVER ;;

void forever(void)
{
        for(EVER)
                ;
}

On peut regarder le code en sortie du pré-processeur :

gcc -E -P boucles.c 

void forever(void)
{
 for(;;)
  ;
}

Et enfin l’assembleur généré :

otool -tv boucles.o 
boucles.o:
(__TEXT,__text) section
_forever:
0000000000000000	pushq	%rbp
0000000000000001	movq	%rsp,%rbp
0000000000000004	jmp	0x00000004

Avis personnel : je met toujours while (1) (ou while (true) en Java). C’est pour moi l’écriture la plus claire à lire et surtout la plus facile à dire.
« Hey machin, j’ai fait une boucle for(;;) !
– Gné ?!
– Oui, un while un quoi !
– Ahhhhh ! »