169 votes

La déclaration d'une variable dans une boucle entraîne-t-elle des frais supplémentaires ? (C++)

Je me demande simplement s'il y aurait une perte de vitesse ou d'efficacité si vous faisiez quelque chose comme ça :

int i = 0;
while(i < 100)
{
    int var = 4;
    i++;
}

qui déclare int var cent fois. Il me semble qu'il y en aurait, mais je n'en suis pas sûr. Ne serait-il pas plus pratique/rapide de faire ceci à la place :

int i = 0;
int var;
while(i < 100)
{
    var = 4;
    i++;
}

ou sont-ils identiques, en termes de vitesse et d'efficacité ?

7 votes

Pour être clair, le code ci-dessus ne "déclare" pas cent fois var.

1 votes

@Rabarberski : La question référencée n'est pas un doublon exact car elle ne précise pas de langue. Cette question est spécifique au C++ . Mais selon les réponses postées à votre question référencée, la réponse dépend du langage et éventuellement du compilateur.

2 votes

@jason Si le premier bout de code ne déclare pas cent fois la variable 'var', pouvez-vous expliquer ce qui se passe ? Est-ce qu'il déclare la variable une fois et l'initialise 100 fois ? J'aurais pensé que le code déclarait et initialisait la variable 100 fois, puisque tout dans la boucle est exécuté 100 fois. Merci.

202voto

laalto Points 50581

L'espace de pile pour les variables locales est généralement alloué dans la portée de la fonction. Donc aucun ajustement du pointeur de pile n'a lieu à l'intérieur de la boucle, il suffit d'assigner 4 à var . Ces deux extraits ont donc la même surcharge.

54 votes

J'aimerais que ces types qui enseignent à l'université connaissent au moins cette chose de base. Une fois, il s'est moqué de moi en déclarant une variable à l'intérieur d'une boucle et je me demandais ce qui n'allait pas jusqu'à ce qu'il cite les performances comme raison de ne pas le faire et je me suis dit "WTF ! ?".

21 votes

Tu es sûr que tu devrais parler de l'espace de la pile tout de suite. Une variable comme celle-ci pourrait aussi être dans un registre.

5 votes

@toto Une variable comme celle-ci pourrait également être nulle part - le var est initialisée mais n'est jamais utilisée, donc un optimiseur raisonnable peut simplement la supprimer complètement (à l'exception du deuxième extrait si la variable a été utilisée quelque part après la boucle).

115voto

Adam Rosenfield Points 176408

Pour les types primitifs et les types POD, cela ne fait aucune différence. Le compilateur allouera l'espace de pile pour la variable au début de la fonction et le désallouera au retour de la fonction dans les deux cas.

Pour les types de classes non-POD qui ont des constructeurs non triviaux, cela VA faire une différence -- dans ce cas, placer la variable en dehors de la boucle n'appellera le constructeur et le destructeur qu'une seule fois et l'opérateur d'affectation à chaque itération, alors que la placer à l'intérieur de la boucle appellera le constructeur et le destructeur à chaque itération de la boucle. En fonction de ce que font le constructeur, le destructeur et l'opérateur d'affectation de la classe, cela peut être souhaitable ou non.

0 votes

Cela ne semble pas vrai. Si vous utilisez un type non POD et que vous l'affectez dans la boucle encore et encore comme dans son exemple, cette affectation à une nouvelle variable appelle également le constructeur encore et encore.

43 votes

Idée correcte, mauvaise raison. Variable en dehors de la boucle. Construite une fois, détruite une fois mais l'opérateur d'alignement est appliqué à chaque itération. Variable à l'intérieur de la boucle. Constructeur/Désatructeur appliqué à chaque itération mais aucune opération d'affectation.

9 votes

C'est la meilleure réponse mais ces commentaires sont déroutants. Il y a une grande différence entre l'appel d'un constructeur et d'un opérateur d'affectation.

71voto

Alex Brown Points 15776

Les deux sont identiques, et voici comment vous pouvez le savoir, en regardant ce que fait le compilateur (même sans que l'optimisation soit réglée à un niveau élevé) :

Regardez ce que le compilateur (gcc 4.0) fait à vos exemples simples :

1.c :

main(){ int var; while(int i < 100) { var = 4; } }

gcc -S 1.c

1.s :

_main:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    $0, -16(%ebp)
    jmp L2
L3:
    movl    $4, -12(%ebp)
L2:
    cmpl    $99, -16(%ebp)
    jle L3
    leave
    ret

2.c

main() { while(int i < 100) { int var = 4; } }

gcc -S 2.c

2.s :

_main:
        pushl   %ebp
        movl    %esp, %ebp
        subl    $24, %esp
        movl    $0, -16(%ebp)
        jmp     L2
L3:
        movl    $4, -12(%ebp)
L2:
        cmpl    $99, -16(%ebp)
        jle     L3
        leave
        ret

A partir de là, vous pouvez voir deux choses : premièrement, le code est le même dans les deux.

Deuxièmement, le stockage de var est alloué en dehors de la boucle :

         subl    $24, %esp

Et finalement, la seule chose dans la boucle est l'affectation et la vérification de la condition :

L3:
        movl    $4, -12(%ebp)
L2:
        cmpl    $99, -16(%ebp)
        jle     L3

Ce qui est à peu près aussi efficace que vous pouvez l'être sans supprimer entièrement la boucle.

2 votes

"Ce qui est à peu près aussi efficace que vous pouvez l'être sans supprimer entièrement la boucle" Pas tout à fait. Un déroulement partiel de la boucle (en la faisant, par exemple, 4 fois par passage) accélérerait considérablement le processus. Il y a probablement de nombreuses autres façons d'optimiser... bien que la plupart des compilateurs modernes se rendraient probablement compte qu'il n'y a aucun intérêt à boucler du tout. Si 'i' est utilisé plus tard, il suffit de mettre 'i' = 100.

0 votes

C'est en supposant que le code a changé pour incrémenter 'i' du tout... tel quel, c'est juste une boucle éternelle.

0 votes

Comme l'était le message original !

14voto

Joshua Points 13231

De nos jours, il est préférable de la déclarer à l'intérieur de la boucle, sauf s'il s'agit d'une constante, car le compilateur pourra mieux optimiser le code (en réduisant la portée des variables).

4 votes

Je doute que cela affecte l'optimisation -- si le compilateur effectue une sorte d'analyse du flux de données, il peut comprendre qu'il n'est pas modifié en dehors de la boucle, donc il devrait produire le même code optimisé dans les deux cas.

3 votes

Il ne le fera pas si vous avez deux boucles différentes utilisant le même nom de variable temporaire.

11voto

Andrew Hare Points 159332

La plupart des compilateurs modernes optimisent cela pour vous. Ceci étant dit, j'utiliserais votre premier exemple car je le trouve plus lisible.

3 votes

Je ne le considère pas vraiment comme une optimisation. Comme il s'agit de variables locales, l'espace de la pile est simplement alloué au début de la fonction. Il n'y a pas de véritable "création" susceptible de nuire aux performances (sauf si un constructeur est appelé, ce qui est une toute autre histoire).

0 votes

Vous avez raison, "optimisation" n'est pas le bon mot, mais je n'en trouve pas de meilleur.

0 votes

Le problème est qu'un tel optimiseur utilisera l'analyse de la portée en temps réel, et que les deux variables sont plutôt mortes.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X