loops for - Y a-t-il des frais généraux pour déclarer une variable dans une boucle?(C++)




11 foreach (12)

Ils sont tous deux les mêmes, et voici comment vous pouvez le découvrir en regardant ce que fait le compilateur (même sans optimisation):

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

À partir de ceux-ci, vous pouvez voir deux choses: premièrement, le code est le même dans les deux cas.

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

         subl    $24, %esp

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

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

Ce qui est à peu près aussi efficace que possible sans enlever complètement la boucle.

Cette question a déjà une réponse ici:

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

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 ne suis pas sûr. Serait-il plus pratique / plus rapide de le faire à la place:

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

ou sont-ils les mêmes, dans le sens de la vitesse et de l'efficacité?


Ces jours-ci, il est préférable de le déclarer dans la boucle sauf s'il s'agit d'une constante car le compilateur sera en mesure de mieux optimiser le code (réduction de la portée variable).

EDIT: Cette réponse est pour la plupart obsolète maintenant. Avec la montée en puissance des compilateurs post-classiques, les cas où le compilateur ne peut pas le comprendre deviennent rares. Je peux toujours les construire mais la plupart des gens classeraient la construction comme un mauvais code.


ce n'est pas vrai il y a des frais généraux cependant sa négligence capable de frais généraux.

Même si probablement ils finiront au même endroit sur la pile. Il l'assigne toujours. Il va assigner l'emplacement de la mémoire sur la pile pour cet int, puis le libérer à la fin de}. Pas dans le sens libre, dans le sens où il déplacera sp (pointeur de pile) par 1. Et dans votre cas, étant donné qu'il n'a qu'une seule variable locale, il équivaudra simplement à fp (frame pointer) et à sp

La réponse courte serait: NE SOYEZ PAS SOYEZ MÊME TRAVAIL PRESQUE LA MÊME.

Mais essayez de lire plus sur la façon dont la pile est organisée. Mon école de premier cycle a eu de très bonnes conférences sur ce sujet Si vous voulez en savoir plus, cliquez ici http://www.cs.utk.edu/~plank/plank/classes/cs360/360/notes/Assembler1/lecture.html


J'ai déjà effectué des tests de performance et, à ma grande surprise, j'ai trouvé que le cas 1 était plus rapide! Je suppose que c'est peut-être parce que déclarer la variable à l'intérieur de la boucle réduit sa portée, donc elle est libérée plus tôt. Cependant, c'était il y a longtemps, sur un très vieux compilateur. Je suis sûr que les compilateurs modernes font un meilleur travail pour optimiser les différences, mais cela ne fait quand même pas de mal de garder votre portée variable aussi courte que possible.


#include <stdio.h>
int main()
{
    for(int i = 0; i < 10; i++)
    {
        int test;
        if(i == 0)
            test = 100;
        printf("%d\n", test);
    }
}

Le code ci-dessus imprime toujours 10 fois 10, ce qui signifie que la variable locale à l'intérieur de la boucle n'est allouée qu'une seule fois par appel de fonction.


Pour un type intégré, il n'y aura probablement aucune différence entre les deux styles (probablement jusqu'au code généré).

Cependant, si la variable est une classe avec un constructeur / destructeur non trivial, il pourrait bien y avoir une différence majeure dans le coût d'exécution. Je placerais généralement la variable à l'intérieur de la boucle (pour garder la portée aussi petite que possible), mais si cela s'avère avoir un impact sur la performance, je chercherais à déplacer la variable de classe en dehors de la portée de la boucle. Cependant, faire cela nécessite une analyse supplémentaire car la sémantique du chemin d'ode peut changer, donc ceci ne peut être fait que si la sémantique le permet.

Une classe RAII pourrait avoir besoin de ce comportement. Par exemple, une classe qui gère la durée de vie des accès aux fichiers peut devoir être créée et détruite à chaque itération de boucle pour gérer correctement l'accès aux fichiers.

Supposons que vous ayez une classe LockMgr qui acquiert une section critique quand elle est construite et la libère lorsqu'elle est détruite:

while (i< 100) {
    LockMgr lock( myCriticalSection); // acquires a critical section at start of
                                      //    each loop iteration

    // do stuff...

}   // critical section is released at end of each loop iteration

est assez différent de:

LockMgr lock( myCriticalSection);
while (i< 100) {

    // do stuff...

}

Je pense que la plupart des réponses manquent un point important à considérer qui est: «Est-ce clair» et évidemment de toute la discussion le fait est; non ce n'est pas. Je suggère dans la plupart des codes de boucle que l'efficacité est à peu près un problème (sauf si vous calculez pour un atterrisseur mars), donc la seule question est ce qui semble plus raisonnable et lisible et maintenable - dans ce cas, je vous recommande de déclarer la variable avant et en dehors de la boucle - cela le rend plus clair. Alors les gens comme vous et moi ne prendraient même pas la peine de perdre du temps à vérifier en ligne pour voir si c'est valide ou non.


Pour les types primitifs et les types POD, cela ne fait aucune différence. Le compilateur alloue l'espace de pile pour la variable au début de la fonction et la libère lorsque la fonction retourne dans les deux cas.

Pour les types de classes non-POD qui ont des constructeurs non-triviaux, cela fera une différence - dans ce cas, mettre la variable en dehors de la boucle n'appellera le constructeur et le destructeur qu'une fois et l'opérateur d'assignation à chaque itération, loop appellera le constructeur et le destructeur pour chaque itération de la boucle. Selon ce que font le constructeur, le destructeur et l'opérateur d'affectation de la classe, cela peut être souhaitable ou non.


Avec seulement deux variables, le compilateur attribuera probablement un registre pour les deux. Ces registres sont là de toute façon, donc cela ne prend pas de temps. Il y a 2 instructions d'écriture de registre et une instruction de lecture de registre dans les deux cas.


Les deux boucles ont la même efficacité. Ils prendront chacun une quantité infinie de temps :) C'est peut-être une bonne idée d'incrémenter je dans les boucles.


La seule façon d'être sûr est de les chronométrer. Mais la différence, s'il y en a une, sera microscopique, vous aurez donc besoin d'une grande boucle de synchronisation.

Plus précisément, le premier est un meilleur style car il initialise la variable var, tandis que l'autre la laisse non initialisée. Ceci et la directive que l'on devrait définir des variables aussi près que possible de leur point d'utilisation signifie que la première forme devrait normalement être préférée.


Dans votre deuxième exemple (avec scanf ()), la raison pour laquelle cela est encore plus lent pourrait être que scanf ("% s") analyse la chaîne et recherche tout espace char (espace, tabulation, retour à la ligne).

En outre, oui, CPython effectue une mise en cache pour éviter les lectures sur disque dur.







c++ loops variable-declaration