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.

9voto

Michael Burr Points 181287

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 mettrais 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 perfectible, j'envisagerais de déplacer la variable de classe en dehors de la portée de la boucle. Cependant, cela nécessite une analyse supplémentaire car la sémantique du chemin de l'ode peut changer, donc cela 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 avoir besoin d'être créée et détruite à chaque itération de la boucle pour gérer correctement l'accès aux fichiers.

Supposons que vous ayez un LockMgr classe qui acquiert une section critique lorsqu'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 très différent de :

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

    // do stuff...

}

7voto

Larry Watanabe Points 7305

Les deux boucles ont la même efficacité. Elles prendront toutes deux un temps infini :) Cela peut être une bonne idée d'incrémenter i à l'intérieur des boucles.

0 votes

Ah oui, j'ai oublié de parler de l'efficacité de l'espace - c'est bon - 2 ints pour les deux. Il me semble simplement étrange que les programmeurs ne voient pas la forêt pour l'arbre - toutes ces suggestions à propos d'un code qui ne se termine pas.

0 votes

Ce n'est pas grave s'ils ne terminent pas. Aucun d'entre eux n'est appelé :-)

0voto

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

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

0 votes

"La seule façon d'être sûr est de les chronométrer." -1 faux. Désolé, mais un autre post a prouvé que cela était faux en comparant le langage machine généré et en le trouvant essentiellement identique. Je n'ai pas de problème avec votre réponse en général, mais n'est-ce pas pour cela que le -1 est faux ?

0 votes

L'examen du code émis est certainement utile, et dans un cas simple comme celui-ci, il peut être suffisant. Cependant, dans des cas plus complexes, des questions telles que la localité de référence se posent, et elles ne peuvent être testées qu'en chronométrant l'exécution.

0voto

Il ne s'agit pas d'une réponse, mais je ne peux pas encore ajouter de commentaire, alors je le poste de cette façon.

@lalto, @Alex Voulez-vous dire que toutes les variables à l'intérieur des fonctions se voient allouer de l'espace au début de la fonction et cela implique-t-il que toutes les variables utilisées à l'intérieur des fonctions utilisent réellement la mémoire jusqu'à la fin de la fonction. Par exemple, dans la fonction suivante

void MyFunction(void) {
 int a;
 ...
 {
  int b;
  ...
 }
 ...
 {
   int c[50];
   ...
 }
 ...
 int d;
 ...
}

1) Voulez-vous dire que a,b,c & d sont tous alloués de l'espace au début de la fonction et la mémoire n'est pas désallouée jusqu'à la fin de la fonction.

2) Pourquoi ne peut-il pas allouer seulement quand c'est nécessaire et désallouer quand c'est hors de portée ?

Merci

-1voto

MSalters Points 74024

Avec seulement deux variables, le compilateur assignera 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.

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