12
votes

Est-ce que l'un d'entre eux utilise plus de ressources que l'autre?

Qu'est-ce qui se passe différemment en arrière-plan de ces deux blocs de code? Serait-on considéré comme "mieux" que l'autre?

Ma pensée est que l'exemple2 pourrait être pire car il pourrait être dû attendre que le collecteur des ordures dispose de l'article, mais je ne sais pas assez sur le collectionneur des ordures pour savoir si cela est vrai. P> Exemple1: P>

for (int i = 1; i <= 32; i++)
{
   ListItem item = new ListItem();
   //do some stuff
}


6 commentaires

Quels tests pouvez-vous écrire pour prouver votre théorie? Et cela importe-t-il vraiment à moins que votre programme devait être très efficace?


Est-ce que ça importe? Oui, parce que je veux savoir par souci de connaissance. Je ne suis pas sûr de quels tests je pouvais écrire, c'est pourquoi je lui ai demandé ici.


Les listitems ont-ils une autre référence autre que l'article?


Que diriez-vous d'envelopper le code dans une minuterie et de courir chacun 1000000 fois et de voir combien de temps ils ont pris? Vous pouvez surveiller l'utilisation de la mémoire si vous vouliez être anal.


Je ne serais pas surpris s'ils conduisent à un code IL identique et exécutent ainsi la même chose. C'est pourquoi c'est agréable d'avoir un décompiler (par exemple ILSPY) pour vérifier / réfuter de telles choses.


Mais considérez ceci: avoir un seul Var SB = New StringBuilder (); et la longueur de réglage répétée = 0 dans la boucle sera une vaste amélioration sur le nouveau StringBuilder (); dans une boucle. Ces opportunités existent donc si vous pouvez réutiliser en toute sécurité une classe existante.


5 Réponses :


2
votes

Mon sentiment personnel serait qu'il y aurait peu de différence ici (au-delà de la différence de scoper évidente).

L'une d'entre elles allouera une nouvelle mémoire avec une référence à celle-ci et laissera la mémoire précédente avec une référence inférieure à une référence, prête à être GC'D comme / si nécessaire.

Je ne pense pas que 2 attendra le GC, en partie parce que le GC n'est pas "en ligne", et partiellement parce qu'il n'y a aucune garantie que élément est la seule référence à l'instance et Exiger GC de toute façon - comme je dis que le nombre de références sera décrémenté et c'est tout.

Bien sûr, c'est juste mon sentiment à ce sujet et je serai intéressé de voir ce que les autres ont à dire (je peux sentir quelques postes de blog peut-être dans l'écriture!)


0 commentaires

4
votes

Cela dépend de ce que "// Est-ce que" // fait des choses "se tient pour.

Dans un programme simple, les deux exemples compileraient au même code d'octet msil.

Mais si dans la boucle, un délégué anonyme, peut-être être exécuté dans un autre fil, est créé qui fait référence à la variable "élément", il importe si "article" était déclaré à l'intérieur ou à l'extérieur de la boucle. Si, comme dans l'exemple 2, "élément" est déclaré à l'intérieur de la boucle, alors lorsque le délégué l'exécute, il verra la valeur de "élément" attribué à l'itération de la boucle qui a créé le délégué (qui est probablement ce qui est destiné à ces cas). Si, comme dans l'exemple 1, "article" a été déclaré en dehors de la boucle, le délégué verra la valeur attribuée au moment de son exécution, qui peut provenir d'une itération ultérieure que celle qui a créé le délégué. Cela provoque des conditions de course déroutantes.

Fondamentalement, bien que le code MSIL octet que c # compile ne représente pas la portée variable, la portée est significative en C # et peut affecter la manière dont ses divers sucres syntaxiques se comportent.


0 commentaires

3
votes

Étonnamment, mes tests montrent l'exemple 2 pour être plus rapide.

sortie. Le cas 1 est une déclaration unique. Cas 2 est une déclaration en boucle. xxx

code: xxx


3 commentaires

Bien que cela me survène maintenant que la question concerne les ressources plutôt que sur le temps.


Les deux méthodes génèrent le même IL (sauf que les variables locales ont des nombres différents), une différence est donc juste en raison d'erreurs statistiques.


Hein. J'ai fait quelques cas A / Essay B Tests récemment et je commence à soupçonner une sorte de mise en cache à faire de la casse b que peu plus rapide. À l'avenir, mes cas seront courus dans un ordre aléatoire. Merci, @svick!



1
votes

Lorsque j'ai conservé // fait des trucs vides, les deux versions générées exactement le même IL, donc il ne peut y avoir de différence possible là-bas. Et je pense que si vous mettez du code là-bas, l'IL généré serait toujours le même.

En ce qui concerne GC, le deuxième exemple pourrait sembler théoriquement plus rapide, car le GC peut collecter le listitem avant d'effectuer l'incrément et la comparaison de la boucle. Mais en fait, cela peut aussi faire la même chose dans le premier cas, alors il n'y a pas de différence.

La seule exception où les deux versions ne sont pas équivalentes est lorsque vous utilisez des fermetures. Dans la première version, une seule variable "instance" est fermée, dans le second cas, il y en a 32. Il existe donc une différence de performance (dans le second cas, l'objet de fermeture est créé pour chaque itération), mais la différence de sens est beaucoup plus importante.

En général, j'aime garder les variables aussi profondes que possible, car je pense que cela aide la readbalité.


1 commentaires

Merci d'avoir souligné mon erreur. J'ai supprimé ma réponse car elle n'a rien contribué à la discussion.



11
votes

J'ai copié votre code dans Visual Studio, le compila, puis regardé l'IL généré. Ceci est l'IL généré par exemple 1: xxx

et il s'agit de l'IL généré par exemple 2: xxx

aussi loin que je comprends Ils sont identiques, à l'exception du fait que les habitants sont déclarés (et donc accessibles) dans l'ordre inverse. Je ne m'attends pas à ce que cela ait un impact sur la performance.


1 commentaires

Merci beaucoup pour ça! Cela me donne confiance en ne pas optimiser mon code. Je sais que c'est un mouvement inutile, mais mon esprit refuse toujours de reconnaître les smarts derrière le compilateur.