Différence entre déclarer les variables avant de les ou en boucle?
Je me suis toujours demandé si, en général, la déclaration d'une poubelle variable avant la boucle, comme opposé à plusieurs reprises à l'intérieur de la boucle, fait tout (performance) différence?
Un (assez inutile) exemple en Java:
a) déclaration avant la boucle:
double intermediateResult;
for(int i=0; i < 1000; i++){
intermediateResult = i;
System.out.println(intermediateResult);
}
b) déclaration (à plusieurs reprises) à l'intérieur de la boucle:
for(int i=0; i < 1000; i++){
double intermediateResult = i;
System.out.println(intermediateResult);
}
Lequel est le mieux, un ou b?
Je soupçonne que l'exposition répétée à la déclaration de la variable (exemple b) crée plus de surcharge en théorie, mais que les compilateurs sont assez intelligents de sorte qu'il n'a pas d'importance. Exemple b a l'avantage d'être plus compact et de limiter la portée de la variable à l'endroit où il est utilisé. Encore, j'ai tendance à code, selon exemple un.
Edit: je suis surtout intéressé par les Java cas.
- C'est important, lors de l'écriture de code Java pour la plateforme Android. Google suggère que, pour des temps critique de code pour déclarer incrémenter des variables à l'extérieur d'une boucle for, comme si à l'intérieur de la boucle for, il déclare à nouveau à chaque fois dans ce type d'environnement. La différence de performances est très perceptible pour cher algorithmes.
- pourriez-vous fournir un lien pour cette suggestion de Google
Vous devez vous connecter pour publier un commentaire.
Qui est mieux, un ou b?
À partir d'un point de vue des performances, vous auriez à le mesurer. (Et à mon avis, si vous pouvez mesurer une différence, le compilateur n'est pas très bon).
À partir d'un point de vue de l'entretien, b est mieux. Déclarer et initialiser des variables dans le même lieu, dans l'étendue la plus étroite possible. Ne pas laisser un trou béant entre la déclaration et l'initialisation, et de ne pas polluer les espaces de noms, vous n'avez pas besoin d'.
Bien j'ai couru votre A et B exemples 20 fois chacun, boucle de 100 millions de fois.(JVM - 1.5.0)
A: moyenne des temps d'exécution: .074 sec
B: moyenne des temps d'exécution : .067 sec
À ma grande surprise, B a été un peu plus rapide.
Aussi vite que les ordinateurs sont désormais il est difficile de dire si vous avez pu mesurer avec précision cette.
Je voudrais le code de la manière, mais je dirais qu'il n'a pas vraiment d'importance.
Cela dépend de la langue et l'utilisation exacte. Par exemple, en C# 1 ne faisait pas de différence. En C# 2, si la variable locale est capturé par une méthode anonyme (ou lambda expression en C# 3) il peut faire une très importants différence.
Exemple:
De sortie:
La différence est que toutes les actions de la capture de la même
outer
variable, mais chacun a sa propreinner
variable.Outer
9?La suivante est ce que j'ai écrit et compilé .NET.
C'est ce que je reçois de .NET Réflecteur quand CIL est rendu en arrière dans le code.
Donc à la fois regarder exactement la même après la compilation. Dans les différentes langues gérées code est converti en CL/byte code et au moment de l'exécution, il est converti en langage machine. Donc, en langage machine d'un double peut même pas être créé sur la pile. Il peut juste être un registre en tant que code de refléter le fait que c'est une variable temporaire pour
WriteLine
fonction. Il y a tout un ensemble de règles d'optimisation, juste pour les boucles. Donc, la personne moyenne ne devriez pas être inquiet à ce sujet, en particulier dans la gestion des langues. Il ya des cas où vous pouvez optimiser gérer du code, par exemple, si vous avez pour concaténer un grand nombre de chaînes en utilisant simplementstring a; a+=anotherstring[i]
vs l'aide deStringBuilder
. Il y a une grande différence de performance entre les deux. Il y a beaucoup de cas où le compilateur ne peut pas optimiser votre code, car il ne peut pas comprendre ce qui est prévu dans une plus grande portée. Mais il peut très bien optimiser les choses de base pour vous.C'est une chasse aux sorcières dans VB.NET. Le Visual Basic raison de ne pas réinitialiser la variable dans cet exemple:
Cela permettra d'imprimer 0 la première fois (Visual Basic variables ont des valeurs par défaut lors de sa déclaration!) mais
i
chaque fois d'après.Si vous ajoutez un
= 0
, cependant, vous obtenez ce que vous pouvez attendre:J'ai fait un test simple:
vs
J'ai compilé ces codes avec gcc - 5.2.0. Et puis j'ai démonté le main ()
de ces deux codes et voilà le résultat:
1º:
vs
2º
Qui sont exactement le même asm résultat. n'est-ce pas une preuve que les deux codes de produire la même chose?
Je serais toujours utiliser Un (plutôt que de compter sur le compilateur) et peut également se réécrire:
Cette limite toujours
intermediateResult
de la boucle du champ d'application, mais n'a pas redeclare lors de chaque itération.Il est dépendant d'une langue - IIRC C# optimise cela, donc il n'y a pas de différence, mais le JavaScript (par exemple) fera l'ensemble de l'allocation de mémoire beau spectacle à chaque fois.
À mon avis, b est la meilleure structure. Dans un, la dernière valeur de intermediateResult autour de bâtons après votre boucle est terminée.
Edit:
Cela ne fait pas beaucoup de différence avec les types de valeur, mais des types de référence peut être un peu lourde. Personnellement, j'aime les variables à être déréférencé dès que possible, pour le nettoyage, et b ne sont que pour vous,
sticks around after your loop is finished
- bien que ce n'est pas grave dans un langage comme Python, où liée noms de rester jusqu'à ce que la fonction se termine.my
mot-clé), C# et Java pour le nom de 5 que j'ai utilisé.Je soupçonne un peu de compilateurs pourrait optimiser à la fois à être le même code, mais certainement pas tous. Je dirais donc que vous êtes mieux avec de l'ancien. La seule raison de ce dernier est si vous voulez vous assurer que la variable déclarée est utilisé seulement au sein de votre boucle.
En règle générale, je déclare mes variables dans l'intérieur de la plus étendue possible. Donc, si vous ne l'utilisez pas intermediateResult à l'extérieur de la boucle, puis j'irais avec B.
Un co-travailleur de la préfère la première forme, en disant qu'il s'agit d'une optimisation, préférant la ré-utilisation d'une déclaration.
Je préfère la seconde (et essayer de convaincre mon collègue! ;-)), avoir lu que:
De toute façon, il tombe dans la catégorie de l'optimisation prématurée qui dépendent de la qualité du compilateur et/ou de la JVM.
Il y a une différence en C# si vous utilisez la variable dans une lambda, etc. Mais en général, le compilateur va faire la même chose, en supposant que la variable n'est utilisée dans la boucle.
Étant donné qu'ils sont fondamentalement les mêmes: Notez que la version b, il est beaucoup plus évident pour les lecteurs que la variable n'est pas, et ne peut pas être utilisé après la boucle. En outre, version b est beaucoup plus facilement remaniée. Il est plus difficile d'extraire le corps de la boucle dans sa propre méthode en version un. En outre, la version b vous assure qu'il n'y a aucun effet secondaire à un tel refactoring.
Par conséquent, la version a me gêne pas de fin, car il n'y a aucun avantage à lui et il les rend beaucoup plus difficile de raisonner sur le code...
Bien, vous pouvez toujours faire une portée pour que:
De cette façon, vous ne déclarez la variable une fois, et il va mourir quand vous sortir de la boucle.
J'ai toujours pensé que si vous déclarez vos variables à l'intérieur de la boucle, alors vous perdez la mémoire. Si vous avez quelque chose comme ceci:
Alors non seulement, l'objet doit être créé pour chaque itération, mais il doit être une nouvelle référence attribué pour chaque objet. Il semble que si le garbage collector est lent, alors vous aurez un tas de dangling références qui ont besoin d'être nettoyé.
Toutefois, si vous avez ceci:
Alors vous êtes seulement à la création d'une référence unique et l'attribution d'un nouvel objet à chaque fois. Bien sûr, il pourrait prendre un peu plus de temps pour elle d'aller hors de portée, mais alors il n'y a qu'un bancales référence pour traiter.
Je pense que ça dépend du compilateur et il est difficile de donner une réponse générale.
Ma pratique est la suivante:
si le type de variable est simple (int, double, ...) je préfère la variante b (à l'intérieur).
Raison: la réduction de portée de variable.
si le type de la variable n'est pas simple (une sorte de
class
oustruct
) je préfère la variante un (à l'extérieur).Raison: en réduisant le nombre de ctor-dtor appels.
À partir d'un point de vue des performances, de l'extérieur est (beaucoup) mieux.
J'ai exécuté les deux fonctions 1 milliard de fois chacun.
en dehors de() a pris de 65 millisecondes. à l'intérieur de() a pris 1,5 secondes.
Un) est fort à parier que B).........Imaginez si vous êtes l'initialisation de la structure en boucle plutôt que de " int " ou "détacher" alors quoi?
comme
Vous êtes certainement tenu à faire face à des problèmes avec des fuites de mémoire!. Donc je crois que 'A' est plus sûr pari tandis que 'B' est vulnérable à la mémoire de l'accumulation de l'esp à travailler au plus près de la source de bibliothèques.Vous pouvez le vérifier usinng 'Valgrind' Outil sur Linux, plus précisément sous l'outil de Helgrind'.
C'est une question intéressante. De mon expérience, il existe une ultime question à prendre en compte lorsque vous débattre de cette question pour un code:
Est-il une raison pourquoi la variable devra être globale?
Il fait sens pour seulement déclarer la variable une fois, à l'échelle mondiale, par opposition à de nombreuses reprises, localement, parce que c'est meilleur pour l'organisation du code et nécessite moins de lignes de code. Toutefois, si elle ne doit être déclarée localement à l'intérieur d'une méthode, je l'initialiser dans la méthode, il est donc clair que la variable est exclusivement pertinents à cette méthode. Attention à ne pas appeler cette variable en dehors de la méthode dans laquelle elle est initialisée si vous choisissez la dernière option--votre code ne sais pas de quoi vous parlez et affiche un message d'erreur.
Aussi, comme une note de côté, ne dupliquez pas les locaux, les noms de variables entre les différentes méthodes, même si leurs buts sont quasi-identiques; il est juste confus.
J'ai testé pour JS avec Nœud 4.0.0 si quelqu'un est intéressé. Déclarant en dehors de la boucle a entraîné une ~.5 ms amélioration de la performance en moyenne plus de 1000 essais avec 100 millions d'itérations de boucle par essai. Donc, je vais dire aller de l'avant et de l'écrire dans le plus lisible et maintenable manière qui est B, de l'omi. Je voudrais mettre mon code dans un violon, mais j'ai utilisé de la performance-maintenant Nœud du module. Voici le code:
c'est la meilleure forme
1) de cette façon, a déclaré une fois de temps variable, et non pas pour chaque cycle.
2) la cession, c'est fatser thean toutes les autres options.
3) Si le bestpractice la règle est toute déclaration à l'extérieur de l'itération pour.
Essayé la même chose en Aller, et par rapport à la sortie du compilateur à l'aide de
go tool compile -S
avec go 1.9.4Différence nulle, que par l'assembleur de sortie.
J'ai eu ce même question pour une longue période de temps. Alors j'ai testé un moyen encore plus simple morceau de code.
Conclusion: Pour de tels cas il est PAS différence de performances.
À l'extérieur de la boucle cas
À l'intérieur de la boucle cas
J'ai vérifié le fichier compilé sur IntelliJ est decompiler et pour les deux cas, j'ai eu l' même
Test.class
J'ai aussi démonté le code pour les deux cas à l'aide de la méthode indiquée dans ce réponse. Je vais vous montrer que les pièces pertinentes à la réponse
À l'extérieur de la boucle cas
À l'intérieur de la boucle cas
Si vous prêtez attention, seuls les
Slot
attribué ài
etintermediateResult
dansLocalVariableTable
est échangé comme un produit de l'ordre de leur apparition. La même différence dans le logement est reflétée dans les autres lignes de code.intermediateResult
est encore une variable locale dans les deux cas, donc, il n'y a pas de différence de temps d'accès.BONUS
Compilateurs faire une tonne d'optimisation, de prendre un coup d'oeil à ce qui se passe dans ce cas.
Zéro cas de travail
Zéro de travail décompilé
Même si je sais que mon compilateur est assez intelligent, je n'aime pas compter sur elle, et utilise une variante.
Le b) de la variante a de sens pour moi que si vous avez désespérément besoin de faire de la intermediateResult indisponible après le corps de la boucle. Mais je ne peux pas imaginer une telle situation désespérée, de toute façon....
EDIT: Jon Skeet fait un très bon point, montrant que la déclaration de la variable à l'intérieur d'une boucle peut faire une réelle différence sémantique.