Différence de performances pour les structures de contrôle 'pour' et 'foreach' en C#
Extrait de code qui donnera de meilleures performances? Le code ci-dessous segments ont été écrit en C#.
1.
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
2.
foreach(MyType current in list)
{
current.DoSomething();
}
- J'imagine qu'il n'a pas vraiment d'importance. Si vous rencontrez des problèmes de performances, il est presque certainement pas à cause de cela. Non pas que vous ne devriez pas vous poser la question...
- À moins que votre application est très critique pour les performances je ne voudrais pas vous inquiéter à ce sujet. Beaucoup mieux de nettoyer et facilement compréhensible par le code.
- Cela m'inquiète que certaines réponses ici semblent être publié par les personnes qui n'ont tout simplement pas le concept d'itérateur n'importe où dans leur cerveau, et donc pas de notion d'agents recenseurs ou les pointeurs.
- Que 2e code ne compile pas. Système.L'objet n'a pas de membre appelé "valeur" (sauf si vous êtes vraiment méchants, ont défini comme une extension de la méthode et de la comparaison de délégués). Vivement le type de votre foreach.
- Le premier code ne compile pas non plus, à moins que le type de
list
vraiment unecount
membre au lieu deCount
. - Doublons: stackoverflow.com/questions/44220/... stackoverflow.com/questions/472191/c-for-vs-foreach
Vous devez vous connecter pour publier un commentaire.
Bien, il dépend en partie du type exact de l'
list
. Elle dépendra aussi de l'exacte CLR que vous utilisez.Si c'est de toute façon significative ou non dépendra de savoir si vous êtes en train de faire tout travail réel dans la boucle. Dans presque tous cas, la différence de performance ne sera pas significative, mais la différence de lisibilité favorise la
foreach
boucle.Je serais personnellement utiliser LINQ pour éviter le "si" de trop:
EDIT: Pour ceux d'entre vous qui prétendez qu'une itération sur un
List<T>
avecforeach
produit le même code que lefor
boucle, voici la preuve qu'il n'a pas d':Produit IL de:
Le compilateur traite tableaux différemment, la conversion d'un
foreach
boucle de coeur pour unfor
boucle, mais pasList<T>
. Voici le code équivalent d'un tableau:Il est intéressant de noter, je ne trouve pas cette documenté dans le C# 3 spec n'importe où...
List<T>
.foreach
sur un tableau est équivalent àfor
de toute façon. Toujours code pour plus de lisibilité d'abord, puis seulement micro-optimiser lorsque vous avez preuve qu'il donne un avantage de performance mesurables.Un
for
boucle est compilé en code approximativement équivalent à ceci:Où, comme un
foreach
boucle est compilé en code approximativement équivalent à ceci:Donc, comme vous pouvez le voir, il serait tout dépend de la façon dont l'agent recenseur est mis en œuvre par rapport à ce que les listes de l'indexeur est mis en œuvre. Il s'avère que l'agent recenseur pour des types de tableaux sont normalement écrit quelque chose comme ceci:
Donc, comme vous pouvez le voir, dans ce cas, il ne fera pas beaucoup de différence, toutefois, l'agent recenseur pour une liste liée serait probablement ressembler à quelque chose comme ceci:
Dans .NET, vous trouverez que la LinkedList<T> class ne dispose même pas d'un indexeur, de sorte que vous ne seriez pas en mesure de faire votre pour la boucle sur une liste liée; mais si vous pouviez, l'indexeur devrait être écrit comme suit:
Comme vous pouvez le voir, l'appeler plusieurs fois dans une boucle va être beaucoup plus lent que d'utiliser un énumérateur qui peut se rappeler où il est dans la liste.
Un simple test pour semi-valider. J'ai fait un petit test, juste pour voir. Voici le code:
Et voici le foreach section:
Quand j'ai remplacé le pour avec un foreach -- le foreach est de 20 millisecondes plus vite - constamment. Le pour a été de 135 139ms tandis que le foreach était de 113-119ms. J'ai échangé avant en arrière plusieurs fois, en s'assurant qu'il n'était pas un processus qui vient de débuter dans.
Cependant, quand j'ai enlevé le foo et de l'instruction if, le pour a été plus rapide en 30 ms (foreach était 88ms et pour a 59 ms). Ils étaient tous les deux des coquilles vides. Je suis en supposant que le foreach effectivement adopté une variable où, comme l'était juste incrémentation d'une variable. Si j'ai ajouté
Puis la pour devenir lent d'environ 30ms. Je suppose que cela a à voir avec elle la création de foo et l'accaparement de la variable dans le tableau et en l'assignant à toto. Si vous venez de l'accès intList[i] alors vous n'avez pas la pénalité.
En toute honnêteté.. je m'attendais à la boucle foreach pour un peu plus de temps, en toutes circonstances, mais pas assez de matière dans la plupart des applications.
edit: voici le nouveau code à l'aide de Jons suggestions (134217728 est le plus int vous pouvez avoir avant du Système.OutOfMemory exception est lancée):
Et voici les résultats:
La production de données.
Le calcul pour la boucle: 2458ms
Calcul de la boucle foreach: 2005ms
Échangeant autour pour voir si elle traite avec l'ordre des choses donne les mêmes résultats (ou presque).
Note: cette réponse s'applique plus à Java qu'en C#, depuis C# n'est pas un indexeur sur
LinkedLists
, mais je pense que le point de vue général tient toujours.Si le
list
vous travaillez avec unLinkedList
, la performance de l'indexeur-code (tableau de style accès) est bien pire qu'à l'aide de laIEnumerator
de laforeach
, pour les grandes listes.Lorsque vous accédez à l'élément de 10.000 dans un
LinkedList
l'utilisation de l'indexeur syntaxe:list[10000]
, la liste liée débutera à la tête de nœud, et traverse laNext
-pointeur dix mille fois, jusqu'à ce qu'il atteigne le bon objet. Évidemment, si vous le faites dans une boucle, vous obtiendrez:Lorsque vous appelez
GetEnumerator
(implicitement à l'aide de laforach
-syntaxe), vous aurez uneIEnumerator
objet qui a un pointeur vers le nœud de tête. Chaque fois que vous appelezMoveNext
, le pointeur est déplacé vers le nœud suivant, comme suit:Comme vous pouvez le voir, dans le cas de
LinkedList
s, le tableau de l'indexeur méthode devient plus lent et plus lent, plus vous en boucle (il doit passer par le même pointeur de tête, encore et encore). Alors que laIEnumerable
juste opère en temps constant.Bien sûr, comme Jon a dit cela dépend vraiment du type de
list
, si lelist
n'est pas unLinkedList
, mais un tableau, le comportement est complètement différent.LinkedList<T>
docs sur MSDN, et il a un assez décent de l'API. Plus important encore, il n'a pas uneget(int index)
méthode, comme Java. Encore, je suppose que le point tient toujours pour toute autre forme de liste structure de données qui expose un indexeur qui est plus lent que sur unIEnumerator
.Comme d'autres personnes l'ont mentionné, bien que la performance n'a pas vraiment d'importance, le foreach sera toujours un peu plus lent en raison de la
IEnumerable
/IEnumerator
utilisation dans la boucle. Le compilateur traduit le construire dans les appels sur cette interface, et pour chaque étape une fonction + une propriété sont appelés, dans l'instruction foreach.C'est l'équivalent de l'expansion de la construction en C#. Vous pouvez imaginer l'impact sur les performances peuvent varier en fonction de la mise en œuvre de la méthode MoveNext et Actuel. Alors que dans un tableau, vous n'avez pas que des dépendances.
List<T>
ici, puis il y a toujours le coup (éventuellement incorporé) de l'appel de l'indexeur. C'est pas comme si c'est un métal nu d'accès au tableau.Après la lecture assez d'arguments que "la boucle foreach doit être préféré pour des raisons de lisibilité", je peux dire que ma première réaction a été "que"? La lisibilité, en général, est subjective et, dans ce cas particulier, même plus. Pour quelqu'un avec une formation en programmation(pratiquement, chaque langue avant de Java), pour les boucles sont beaucoup plus faciles à lire que les boucles foreach. En outre, les mêmes personnes qui prétendent que les boucles foreach sont plus lisibles, sont aussi des partisans de linq et d'autres "fonctions" que de rendre le code difficile à lire et à maintenir, quelque chose qui prouve que le point ci-dessus.
À propos de l'impact sur les performances, voir la réponse à la cette question.
EDIT: Il y a des collections en C#(comme le HashSet) qui n'ont pas d'indexation. Dans ces collections, foreach est le seul moyen pour parcourir et c'est le seul cas je pense qu'il devrait être utilisé plus de pour.
Il est un autre fait intéressant qui peut être facilement manqué lors du test de la vitesse de chacune des deux boucles:
En utilisant le mode de débogage ne pas laisser le compilateur d'optimiser le code en utilisant les paramètres par défaut.
Cela m'a conduit au résultat intéressant que le foreach est plus rapide qu'en mode de débogage. Alors que l'eis plus vite que foreach dans le mode de libération. Évidemment, le compilateur a de meilleures façons d'optimiser une boucle for que d'une boucle foreach qui compromet plusieurs appels de méthode. Une boucle for est par l'utilisation de ces fondamentaux que c'est possible que ce soit optimisé par le PROCESSEUR lui-même.
Dans l'exemple fourni par vous, il est certainement préférable d'utiliser
foreach
boucle au lieu d'unfor
boucle.La norme
foreach
construction peut être plus rapide (1,5 cycles par étape) qu'un simplefor-loop
(2 cycles par étape), à moins que la boucle a été déroulée (1.0 cycles par étape).Donc pour tous les jours de code, la performance n'est pas une raison pour utiliser le plus complexe
for
,while
oudo-while
constructions.Consultez ce lien: http://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
vous pouvez lire à ce sujet dans De profondeur .NET - partie 1 Itération
c'est de couvrir l'ensemble des résultats (sans la première initialisation) à partir de .NET code source de tout le chemin pour le démontage.
par exemple de Tableau d'Itération avec une boucle foreach:
et - liste itération avec boucle foreach:
et les résultats finaux: