Je me suis toujours demandé si, en général, déclarer une variable jetable avant une boucle, par opposition à plusieurs reprises à l'intérieur de la boucle, faisait une différence (de performance)? Un exemple (assez inutile) en Java:
a) déclaration avant 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 meilleur, a ou b ?
Je soupçonne que la déclaration de variable répétée (exemple b ) crée plus de surcharge en théorie , mais que les compilateurs sont suffisamment intelligents pour que cela n'ait pas d'importance. L'exemple b a l'avantage d'être plus compact et de limiter la portée de la variable à l'endroit où elle est utilisée. Pourtant, j'ai tendance à coder selon l'exemple a .
Edit: je suis particulièrement intéressé par le cas Java.
la source
Réponses:
Quel est le meilleur, a ou b ?
Du point de vue des performances, il faudrait le mesurer. (Et à mon avis, si vous pouvez mesurer une différence, le compilateur n'est pas très bon).
Du point de vue de la maintenance, b est meilleur. Déclarez et initialisez les variables au même endroit, dans la portée la plus étroite possible. Ne laissez pas de trou béant entre la déclaration et l'initialisation, et ne polluez pas les espaces de noms dont vous n'avez pas besoin.
la source
Eh bien, j'ai exécuté vos exemples A et B 20 fois chacun, en bouclant 100 millions de fois. (JVM - 1.5.0)
A: temps d'exécution moyen: 0,074 sec
B: temps d'exécution moyen: 0,067 sec
À ma grande surprise, B était légèrement plus rapide. Aussi vite que les ordinateurs sont maintenant difficiles à dire si vous pouvez mesurer avec précision cela. Je le coderais également de la manière A, mais je dirais que cela n'a pas vraiment d'importance.
la source
Cela dépend de la langue et de l'utilisation exacte. Par exemple, en C # 1, cela ne faisait aucune différence. En C # 2, si la variable locale est capturée par une méthode anonyme (ou expression lambda en C # 3), cela peut faire une différence très significative.
Exemple:
Production:
La différence est que toutes les actions capturent la même
outer
variable, mais chacune a sa propreinner
variable distincte .la source
Outer
avoir 9 ans?Voici ce que j'ai écrit et compilé dans .NET.
C'est ce que j'obtiens de .NET Reflector lorsque CIL est rendu en code.
Les deux se ressemblent donc exactement après la compilation. Dans les langages gérés, le code est converti en code CL / octet et au moment de l'exécution, il est converti en langage machine. Ainsi, en langage machine, un double peut même ne pas être créé sur la pile. Il peut simplement s'agir d'un registre car le code indique qu'il s'agit d'une variable temporaire pour la
WriteLine
fonction. Il existe un ensemble de règles d'optimisation pour les boucles. Donc, le gars moyen ne devrait pas s'en inquiéter, surtout dans les langues gérées. Il existe des cas où vous pouvez optimiser la gestion du code, par exemple, si vous devez concaténer un grand nombre de chaînes en utilisant simplementstring a; a+=anotherstring[i]
vs en utilisantStringBuilder
. Il y a une très grande différence de performances entre les deux. Il existe de nombreux 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 cela peut à peu près optimiser les choses de base pour vous.la source
Il s'agit d'un piège dans VB.NET. Le résultat Visual Basic ne réinitialise pas la variable dans cet exemple:
Cela affichera 0 la première fois (les variables Visual Basic ont des valeurs par défaut lorsqu'elles sont déclarées!) Mais à
i
chaque fois par la suite.Si vous ajoutez un
= 0
, cependant, vous obtenez ce que vous attendez:la source
J'ai fait un test simple:
contre
J'ai compilé ces codes avec gcc - 5.2.0. Et puis j'ai démonté le principal () de ces deux codes et c'est le résultat:
1º:
contre
2º
Qui sont exactement le même résultat asm. n'est-ce pas une preuve que les deux codes produisent la même chose?
la source
Cela dépend du langage - IIRC C # optimise cela, donc il n'y a pas de différence, mais JavaScript (par exemple) fera à chaque fois l'intégralité de l'allocation de mémoire.
la source
J'utiliserais toujours A (plutôt que de compter sur le compilateur) et je pourrais également réécrire:
Cela se limite toujours
intermediateResult
à la portée de la boucle, mais ne se redéclare pas à chaque itération.la source
À mon avis, b est la meilleure structure. Dans un, la dernière valeur de intermédiaireRésultat reste après la fin de votre boucle.
Modifier: cela ne fait pas beaucoup de différence avec les types de valeur, mais les types de référence peuvent être quelque peu lourds. Personnellement, j'aime que les variables soient déréférencées dès que possible pour le nettoyage, et b le fait pour vous,
la source
sticks around after your loop is finished
- même si cela n'a pas d'importance dans un langage comme Python, où les noms liés restent jusqu'à la fin de la fonction.my
mot - clé), C # et Java pour nommer 5 que j'ai utilisé.Je soupçonne que quelques compilateurs pourraient optimiser les deux pour être le même code, mais certainement pas tous. Je dirais donc que vous êtes mieux avec l'ancien. La seule raison de ce dernier est si vous voulez vous assurer que la variable déclarée est utilisée uniquement dans votre boucle.
la source
En règle générale, je déclare mes variables dans la portée la plus interne possible. Donc, si vous n'utilisez pas intermédiaireRésultat en dehors de la boucle, alors j'irais avec B.
la source
Un collègue préfère le premier formulaire, disant qu'il s'agit d'une optimisation, préférant réutiliser une déclaration.
Je préfère le second (et j'essaie de convaincre mon collègue! ;-)), après avoir lu cela:
Quoi qu'il en soit, il tombe dans la catégorie de l'optimisation prématurée qui repose sur la qualité du compilateur et / ou de la JVM.
la source
Il y a une différence en C # si vous utilisez la variable dans un lambda, etc. Mais en général, le compilateur fera essentiellement la même chose, en supposant que la variable n'est utilisée que dans la boucle.
Étant donné qu'ils sont fondamentalement les mêmes: notez que la version b rend beaucoup plus évident pour les lecteurs que la variable n'est pas et ne peut pas être utilisée après la boucle. De plus, la version b est beaucoup plus facilement refactorisée. Il est plus difficile d'extraire le corps de la boucle dans sa propre méthode dans la version a.De plus, la version b vous assure qu'il n'y a aucun effet secondaire à un tel refactoring.
Par conséquent, la version a me dérange sans fin, car elle ne présente aucun avantage et rend beaucoup plus difficile de raisonner sur le code ...
la source
Eh bien, vous pouvez toujours créer une portée pour cela:
De cette façon, vous ne déclarez la variable qu'une seule fois, et elle mourra lorsque vous quitterez la boucle.
la source
J'ai toujours pensé que si vous déclarez vos variables à l'intérieur de votre boucle, vous perdez de la mémoire. Si vous avez quelque chose comme ça:
Ensuite, non seulement l'objet doit être créé pour chaque itération, mais il doit y avoir une nouvelle référence allouée pour chaque objet. Il semble que si le garbage collector est lent, vous aurez un tas de références pendantes qui doivent être nettoyées.
Cependant, si vous avez ceci:
Ensuite, vous ne créez qu'une seule référence et lui attribuez un nouvel objet à chaque fois. Bien sûr, cela peut prendre un peu plus de temps pour sortir de la portée, mais il n'y a alors qu'une seule référence pendante à traiter.
la source
Je pense que cela dépend du compilateur et qu'il est difficile de donner une réponse générale.
la source
Ma pratique est la suivante:
si le type de variable est simple (int, double, ...) je préfère la variante b (à l'intérieur).
Motif: réduction de la portée de la variable.
si le type de variable n'est pas simple (une sorte de
class
oustruct
) je préfère la variante a (extérieur).Raison: réduction du nombre d'appels ctor-dtor.
la source
Du point de vue des performances, l'extérieur est (beaucoup) mieux.
J'ai exécuté les deux fonctions 1 milliard de fois chacune. outside () a pris 65 millisecondes. inside () a pris 1,5 seconde.
la source
J'ai testé JS avec Node 4.0.0 si quelqu'un est intéressé. La déclaration en dehors de la boucle a entraîné une amélioration des performances de ~ 0,5 ms en moyenne sur 1000 essais avec 100 millions d'itérations de boucle par essai. Je vais donc dire allez-y et écrivez-le de la manière la plus lisible / maintenable qui soit B, imo. Je mettrais mon code dans un violon, mais j'ai utilisé le module Node performance-now. Voici le code:
la source
A) est une valeur sûre que B) ......... Imaginez si vous initialisez la structure en boucle plutôt que «int» ou «float» alors quoi?
comme
Vous êtes certainement lié à des problèmes de fuites de mémoire !. Par conséquent, je pense que «A» est un pari plus sûr tandis que «B» est vulnérable à l'accumulation de mémoire, notamment en travaillant dans les bibliothèques de sources proches.
la source
C'est une question intéressante. D'après mon expérience, il y a une question ultime à considérer lorsque vous débattez de cette question pour un code:
Y a-t-il une raison pour laquelle la variable devrait être globale?
Il est logique de ne déclarer la variable qu'une seule fois, globalement, par opposition à plusieurs fois localement, car elle est meilleure pour organiser le code et nécessite moins de lignes de code. Cependant, si elle doit seulement être déclarée localement dans une méthode, je l'initialiserais dans cette méthode afin qu'il soit clair que la variable est exclusivement pertinente pour cette méthode. Veillez à ne pas appeler cette variable en dehors de la méthode dans laquelle elle est initialisée si vous choisissez cette dernière option - votre code ne saura pas de quoi vous parlez et signalera une erreur.
De plus, en remarque, ne dupliquez pas les noms de variables locales entre différentes méthodes même si leurs objectifs sont presque identiques; ça devient confus.
la source
c'est la meilleure forme
1) a ainsi déclaré une fois fois les deux variables, et non chacune pour le cycle. 2) l'affectation est plus grosse que toutes les autres options. 3) La règle de la meilleure pratique est donc toute déclaration en dehors de l'itération pour.
la source
J'ai essayé la même chose dans Go, et j'ai comparé la sortie du compilateur en utilisant
go tool compile -S
avec go 1.9.4Zéro différence, selon la sortie de l'assembleur.
la source
J'avais cette même question depuis longtemps. J'ai donc testé un morceau de code encore plus simple.
Conclusion: pour de tels cas, il n'y a PAS différence de performance.
Cas de boucle extérieure
Étui à boucle intérieure
J'ai vérifié le fichier compilé sur le décompilateur d'IntelliJ et pour les deux cas, j'ai obtenu le même
Test.class
J'ai également démonté le code pour les deux cas en utilisant la méthode indiquée dans cette réponse . Je ne montrerai que les parties pertinentes pour la réponse
Cas de boucle extérieure
Étui à boucle intérieure
Si vous y prêtez attention, seuls les éléments
Slot
affectés ài
etintermediateResult
dansLocalVariableTable
sont échangés en tant que produit de leur ordre d'apparition. La même différence de slot se reflète dans les autres lignes de code.intermediateResult
est toujours une variable locale dans les deux cas, il n'y a donc pas de différence de temps d'accès.PRIME
Les compilateurs font une tonne d'optimisation, regardez ce qui se passe dans ce cas.
Cas de travail zéro
Zéro travail décompilé
la source
Même si je sais que mon compilateur est assez intelligent, je n'aimerai pas m'y fier et utiliserai la variante a).
La variante b) n'a de sens pour moi que si vous avez désespérément besoin de rendre le résultat intermédiaire non disponible après le corps de la boucle. Mais je ne peux pas imaginer une situation aussi désespérée, de toute façon ...
EDIT: Jon Skeet a fait un très bon point, montrant que la déclaration de variable à l'intérieur d'une boucle peut faire une réelle différence sémantique.
la source