while (1) Vs. for (;;) y a-t-il une différence de vitesse?

Version Longue...

, Un collègue a affirmé aujourd'hui, après avoir vu mon utilisation de while (1) dans un script Perl for (;;) est plus rapide. J'ai soutenu qu'ils devraient être les mêmes en espérant que l'interprète optimiserait les différences. J'ai mis en place un script qui exécuterait 1 000 000 000 pour les itérations de boucle et le même nombre de boucles while et enregistrerait le temps entre. Je ne pouvais trouver aucune différence appréciable. Mon collègue a dit qu'un professeur lui avait dit que le while (1) faisait une comparaison 1 == 1 et le for (;;) ne l'était pas. Nous avons répété le même test avec le 100x le nombre d'itérations avec C++ et la différence était négligeable. C'était cependant un exemple graphique de combien le code compilé peut être plus rapide qu'un langage de script.

Version courte...

Y a-t-il une raison de préférer un while (1) à un for (;;) si vous avez besoin d'une boucle infinie pour sortir?

Remarque:, Si ce n'est pas clair à partir de la question. Ce fut purement une discussion académique amusant entre un couple des amis. Je suis conscient que ce n'est pas un concept super important que tous les programmeurs devraient agoniser. Merci pour toutes les bonnes réponses que j'AI (et je suis sûr que d'autres) ont appris quelques choses de cette discussion.

Mise à jour: le collègue susmentionné a pesé avec une réponse ci-dessous.

Cité ici au cas où il serait enterré.

Il provient d'un programmeur d'assemblage AMD. Il a déclaré que c programmeurs (le poeple) ne réalisent pas que leur code a inefficacité. Il a dit aujourd'hui cependant, les compilateurs gcc sont très bons, et mettent des gens comme lui de l'entreprise. Il a dit par exemple, et m'a parlé de la while 1 vs for(;;). Je l'utilise maintenant par habitude mais gcc et surtout les interprètes fera la même opération (un saut de processeur) pour ces deux jours, depuis qu'ils sont optimisés.

141
demandé sur hippietrail 2009-05-20 06:34:08

20 réponses

En perl, ils aboutissent aux mêmes opcodes:

$ perl -MO=Concise -e 'for(;;) { print "foo\n" }'
a  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 2 -e:1) v ->3
9     <2> leaveloop vK/2 ->a
3        <{> enterloop(next->8 last->9 redo->4) v ->4
-        <@> lineseq vK ->9
4           <;> nextstate(main 1 -e:1) v ->5
7           <@> print vK ->8
5              <0> pushmark s ->6
6              <$> const[PV "foo\n"] s ->7
8           <0> unstack v ->4
-e syntax OK

$ perl -MO=Concise -e 'while(1) { print "foo\n" }'
a  <@> leave[1 ref] vKP/REFC ->(end)
1     <0> enter ->2
2     <;> nextstate(main 2 -e:1) v ->3
9     <2> leaveloop vK/2 ->a
3        <{> enterloop(next->8 last->9 redo->4) v ->4
-        <@> lineseq vK ->9
4           <;> nextstate(main 1 -e:1) v ->5
7           <@> print vK ->8
5              <0> pushmark s ->6
6              <$> const[PV "foo\n"] s ->7
8           <0> unstack v ->4
-e syntax OK

De même dans GCC:

#include <stdio.h>

void t_while() {
    while(1)
        printf("foo\n");
}

void t_for() {
    for(;;)
        printf("foo\n");
}

    .file   "test.c"
    .section    .rodata
.LC0:
    .string "foo"
    .text
.globl t_while
    .type   t_while, @function
t_while:
.LFB2:
    pushq   %rbp
.LCFI0:
    movq    %rsp, %rbp
.LCFI1:
.L2:
    movl    $.LC0, %edi
    call    puts
    jmp .L2
.LFE2:
    .size   t_while, .-t_while
.globl t_for
    .type   t_for, @function
t_for:
.LFB3:
    pushq   %rbp
.LCFI2:
    movq    %rsp, %rbp
.LCFI3:
.L5:
    movl    $.LC0, %edi
    call    puts
    jmp .L5
.LFE3:
    .size   t_for, .-t_for
    .section    .eh_frame,"a",@progbits
.Lframe1:
    .long   .LECIE1-.LSCIE1
.LSCIE1:
    .long   0x0
    .byte   0x1
    .string "zR"
    .uleb128 0x1
    .sleb128 -8
    .byte   0x10
    .uleb128 0x1
    .byte   0x3
    .byte   0xc
    .uleb128 0x7
    .uleb128 0x8
    .byte   0x90
    .uleb128 0x1
    .align 8
.LECIE1:
.LSFDE1:
    .long   .LEFDE1-.LASFDE1
.LASFDE1:
    .long   .LASFDE1-.Lframe1
    .long   .LFB2
    .long   .LFE2-.LFB2
    .uleb128 0x0
    .byte   0x4
    .long   .LCFI0-.LFB2
    .byte   0xe
    .uleb128 0x10
    .byte   0x86
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI1-.LCFI0
    .byte   0xd
    .uleb128 0x6
    .align 8
.LEFDE1:
.LSFDE3:
    .long   .LEFDE3-.LASFDE3
.LASFDE3:
    .long   .LASFDE3-.Lframe1
    .long   .LFB3
    .long   .LFE3-.LFB3
    .uleb128 0x0
    .byte   0x4
    .long   .LCFI2-.LFB3
    .byte   0xe
    .uleb128 0x10
    .byte   0x86
    .uleb128 0x2
    .byte   0x4
    .long   .LCFI3-.LCFI2
    .byte   0xd
    .uleb128 0x6
    .align 8
.LEFDE3:
    .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
    .section    .note.GNU-stack,"",@progbits

Donc, je suppose que la réponse est, ils sont les mêmes dans de nombreux compilateurs. Bien sûr, pour d'autres compilateurs, ce n'est peut-être pas nécessairement le cas, mais il y a de fortes chances que le code à l'intérieur de la boucle soit quelques milliers de fois plus cher que la boucle elle-même, alors qui s'en soucie?

210
répondu bdonlan 2009-05-20 02:54:33

En utilisant GCC, ils semblent tous les deux compiler dans le même langage d'assemblage:

L2:
        jmp     L2
55
répondu Martin Cote 2009-05-20 02:48:44

Il n'y a pas beaucoup de raisons de préférer l'un à l'autre. Je pense que while(1) et en particulier while(true) sont plus lisibles que for(;;), mais c'est juste ma préférence.

50
répondu Bill the Lizard 2009-05-20 02:38:46

Il n'y a pas de différence selon la norme. 6.5.3 / 1 A:

L'instruction for

for ( for-init-statement ; conditionopt ; expressionopt ) statement

Est équivalent à

{
  for-init-statement
  while ( condition ) {
    statement
    expression ;
  }
}

Et 6.5.3/2 a:

L'une ou l'autre des conditions et l'expression peuvent être omises. Une condition manquante rend la clause implicite while équivalente à while (true).

Donc, selon la norme c++, le code:

for (;;);

Est exactement le même que:

{
  while (true) {
    ;
    ;
  }
}
31
répondu Richard Corden 2015-02-17 19:53:15

Le compilateur Visual C++ Utilisé pour émettre un avertissement pour

while (1) 

(expression constante), mais pas pour

for (;;)

J'ai continué la pratique de préférer for (;;) pour cette raison, mais je ne sais pas si le compilateur le fait encore ces jours-ci.

28
répondu sean e 2011-09-03 18:05:16

for(;;) est-ce qu'un caractère de moins à taper si vous voulez aller dans cette direction pour optimiser les choses.

26
répondu Chris Bartow 2011-09-03 18:06:55

Turbo C avec ces anciens compilateurs for(;;) entraîne un code plus rapide que while(1).

Aujourd'hui gcc, les compilateurs Visual C (je pense que presque tous) optimisent bien, et les processeurs avec 4.7 MHz sont rarement utilisés.

À cette époque, un for( i=10; i; i-- ) était plus rapide que for( i=1; i <=10; i++ ), car comparer i est 0, entraîne un saut conditionnel CPU-Zéro-drapeau. Et le drapeau Zéro a été modifié avec la dernière opération de décrémentation ( i-- ), aucune opération cmp supplémentaire n'est nécessaire.

    call    __printf_chk
    decl    %ebx          %ebx=iterator i 
    jnz     .L2
    movl    -4(%ebp), %ebx
    leave

Et ici avec for(i=1; i<=10; i++), avec supplément cmpl:

    call    __printf_chk
    incl    %ebx
    cmpl    $11, %ebx
    jne     .L2
    movl    -4(%ebp), %ebx
    leave
20
répondu Lutz L. 2014-07-23 03:12:49

Pour toutes les personnes qui se disputent, vous ne devriez pas utiliser des boucles indéfinies while, Et suggérer des trucs stupides comme utiliser open goto ( sérieusement, aïe)

while (1) {
     last if( condition1 );
     code();
     more_code(); 
     last if( condition2 ); 
     even_more_code(); 
}

Ne peut pas vraiment être représenté efficacement d'une autre manière. Non sans créer une variable de sortie et faire de la magie noire pour la synchroniser.

Si vous avez un penchant pour la syntaxe plus goto-esque, utilisez quelque chose de sain d'esprit qui limite la portée.

flow: { 

   if ( condition ){ 
      redo flow;
   }
   if ( othercondition ){ 
       redo flow;
   }
   if ( earlyexit ){ 
       last flow;
   }
   something(); # doesn't execute when earlyexit is true 
}

En fin de compte, la vitesse n'est pas si importante

S'inquiéter de l'efficacité des différentes constructions en boucle en termes de vitesse est une perte de temps massive. Optimisation prématurée à travers et à travers. Je ne peux pas penser à une situation que j'ai jamais vue où le code de profilage a trouvé des goulots d'étranglement dans mon choix de construction en boucle.

Généralement son la comment de la boucle et de la ce de la boucle.

Vous devriez "optimiser" pour la lisibilité et la concision, et écrire ce qui est le mieux pour expliquer le problème au suivant pauvre suceur qui trouve votre code.

Si vous utilisez le truc" Goto LABEL " mentionné par quelqu'un, et que je dois utiliser votre code, soyez prêt à dormir avec un œil ouvert, surtout si vous le faites plus d'une fois, parce que ce genre de choses crée horrible code spaghetti.

Juste parce que vous peut créer du code spaghetti ne signifie pas que vous devraient

13
répondu Kent Fredric 2009-05-20 04:51:30

De Stroustrup, TC++PL (3ème édition), §6.1.1:

La notation curieuse for (;;) est la manière standard de spécifier une boucle infinie; vous pouvez la prononcer "pour toujours". [...] while (true) est une alternative.

Je préfère for (;;).

8
répondu Hans W 2010-02-02 08:54:27

J'en ai entendu parler une fois.

Il provient d'un programmeur d'assemblage AMD. Il a déclaré que les programmeurs C (Les gens) ne réalisent pas que leur code a des inefficacités. Il a dit aujourd'hui cependant, les compilateurs gcc sont très bons, et mettent des gens comme lui hors d'affaires. Il a dit par exemple, et m'a parlé de la while 1 vs for(;;). Je l'utilise maintenant par habitude mais gcc et surtout les interprètes feront la même opération (un saut de processeur) pour ces deux jours, car ils sont optimisés.

8
répondu Jimmie Clark 2014-07-23 03:09:27

Si le compilateur ne fait aucune optimisation, {[1] } serait toujours plus rapide que while(true). En effet, while-statement évalue la condition à chaque fois, mais for-statement est un saut inconditionnel. Mais si le compilateur optimise le flux de contrôle, il peut générer des opcodes. Vous pouvez lire le code de démontage très facilement.

P.S. vous pourriez écrire une boucle infinie comme ceci:

#define EVER ;;
  //...
  for (EVER) {
    //...
  }
8
répondu silverbullettt 2014-07-23 18:14:12

Dans une construction optimisée d'un langage compilé, il ne devrait pas y avoir de différence appréciable entre les deux. Ni ne devrait finir par effectuer des comparaisons à l'exécution, ils exécuteront simplement le code de boucle jusqu'à ce que vous quittiez manuellement la boucle (par exemple avec un break).

5
répondu Charlie 2009-05-20 02:39:42

Je suis surpris que personne n'correctement testé for (;;) contre while (1) en perl!

Parce que perl est un langage interprété, le temps d'exécution d'un script perl ne consiste pas seulement en la phase d'exécution (qui dans ce cas est la même) mais aussi en la phase d'interprétation avant l'exécution. Ces deux phases doivent être prises en compte lors de la comparaison de vitesse.

Heureusement, perl a un module Benchmark pratique que nous pouvons utiliser pour implémenter un benchmark tel que suit:

#!/usr/bin/perl -w

use Benchmark qw( cmpthese );

sub t_for   { eval 'die; for (;;) { }'; }
sub t_for2  { eval 'die; for (;;)  { }'; }
sub t_while { eval 'die; while (1) { }'; }

cmpthese(-60, { for => \&t_for, for2 => \&t_for2, while => \&t_while });

Notez que je teste deux versions différentes de la boucle infinite for: une qui est plus courte que la boucle while et une autre qui a un espace supplémentaire pour la rendre de la même longueur que la boucle while.

Sur Ubuntu 11.04 x86_64 avec perl 5.10.1 j'obtiens les résultats suivants:

          Rate   for  for2 while
for   100588/s    --   -0%   -2%
for2  100937/s    0%    --   -1%
while 102147/s    2%    1%    --

La boucle while est clairement le gagnant sur cette plate-forme.

Sur FreeBSD 8.2 x86_64 avec perl 5.14.1:

         Rate   for  for2 while
for   53453/s    --   -0%   -2%
for2  53552/s    0%    --   -2%
while 54564/s    2%    2%    --

Alors que loop est le gagnant ici aussi.

Sur FreeBSD 8.2 i386 avec perl 5.14.1:

         Rate while   for  for2
while 24311/s    --   -1%   -1%
for   24481/s    1%    --   -1%
for2  24637/s    1%    1%    --

Étonnamment, la boucle for avec un espace supplémentaire est le choix le plus rapide ici!

Ma conclusion est que la boucle while devrait être utilisée sur la plate-forme x86_64 si le programmeur optimise la vitesse. Évidemment une boucle for doit être utilisé lors de l'optimisation de l'espace. Mes résultats sont malheureusement peu concluants concernant d'autres plates-formes.

3
répondu snap 2011-09-03 17:58:19

En théorie, un compilateurcomplètement naïf pourrait stocker le littéral ' 1 ' dans le binaire (perdre de l'espace) et vérifier si 1 = = 0 chaque itération (perdre du temps et plus d'espace).

En réalité, même avec des optimisations "non", les compilateurs réduiront toujours les deux à la même chose. Ils peuvent également émettre des avertissements car cela pourrait indiquer une erreur logique. Par exemple, l'argument de while pourrait être défini ailleurs et vous ne réalisez pas qu'il est constant.

2
répondu Nick T 2010-04-16 14:23:31

Je suis surpris que personne n'ait offert la forme la plus directe, correspondant à l'assemblage souhaité:

forever:
     do stuff;
     goto forever;
2
répondu Phil Miller 2010-08-17 19:10:10

while(1) est un idiome pour for(;;), qui est reconnu par la plupart des compilateurs.

J'étais heureux de voir que perl reconnaît until(0), aussi.

2
répondu J M D 2014-07-23 03:06:54

Pour résumer le débat for (;;) vs while (1) Il est évident que le premier était plus rapide à l'époque des anciens compilateurs non optimisés, c'est pourquoi vous avez tendance à le voir dans les anciennes bases de code telles que Lions Unix source code commentary, cependant à l'ère des compilateurs d'optimisation badass ces gains sont optimisés en couplant cela avec le fait que ce dernier est plus facile à comprendre que le premier, je crois que ce serait plus préférable.

2
répondu redbandit 2016-06-13 10:06:50

Vient de tomber sur ce fil (bien que quelques années de retard).

Je pense avoir trouvé la raison réelle pour laquelle "for (;;)" Est meilleur que " while (1)".

Selon la "norme de codage barr 2018"

Kernighan & Ritchie long ago recommended for (;;) , which has the additional benefit
of insuring against the visually-confusing defect of a while (l); referencing a variable ‘l’.

Fondamentalement, ce n'est pas un problème de vitesse mais un problème de lisibilité. Selon la police / l'impression du code, le numéro un (1) dans un certain temps peut ressembler à une lettre minuscule L.

C'est-à-dire 1 vs L. (Dans certaines polices, celles-ci semblent identiques).

Alors que (1) peut ressembler à certains alors que la boucle dépend de la lettre variable L.

While (true)peut également fonctionner mais dans certains cas c plus anciens et c embarqués, true / false ne sont pas encore définis à moins que stdbool.h est inclus.

1
répondu Nick Law 2018-09-06 09:55:51

Je pense que les deux sont les mêmes en termes de performance. Mais je préférerais while (1) pour la lisibilité mais je me demande pourquoi vous avez besoin d'une boucle infinie.

-3
répondu bichonfrise74 2009-05-21 05:13:37

Ils sont les mêmes. Il y a des questions beaucoup plus importantes à réfléchir.

-12
répondu Mark Ransom 2009-05-20 02:38:51