Dans .NET, quelle boucle s'exécute plus rapidement, «for» ou «foreach»?

345

En C # / VB.NET / .NET, quelle boucle s'exécute plus rapidement, forou foreach?

Depuis que j'ai lu qu'une forboucle fonctionne plus rapidement qu'une foreachboucle il y a longtemps, j'ai supposé qu'elle était vraie pour toutes les collections, les collections génériques, tous les tableaux, etc.

J'ai parcouru Google et trouvé quelques articles, mais la plupart d'entre eux ne sont pas concluants (lire les commentaires sur les articles) et ouverts.

L'idéal serait de répertorier chaque scénario et la meilleure solution pour le même.

Par exemple (juste un exemple de comment cela devrait être):

  1. pour itérer un tableau de plus de 1000 chaînes - forest mieux queforeach
  2. pour itérer sur IListdes chaînes (non génériques) - foreachest mieux quefor

Quelques références trouvées sur le web pour les mêmes:

  1. Grand ancien article original d'Emmanuel Schanzer
  2. CodeProject FOREACH Vs. POUR
  3. Blog - To foreachto not to foreach, telle est la question
  4. Forum ASP.NET - NET 1.1 C # forvsforeach

[Éditer]

Hormis l'aspect lisibilité de celui-ci, je suis vraiment intéressé par les faits et les chiffres. Il existe des applications où le dernier kilomètre d'optimisation des performances est important.

Binoj Antony
la source
3
La différence existe toujours. Les tableaux en particulier devraient être tout aussi rapides sous foreach, mais pour tout le reste, les boucles simples sont plus rapides. Bien sûr, la plupart du temps, cela ne fera aucune différence, et bien sûr, un compilateur JIT intelligent pourrait en théorie éliminer la différence.
jalf
3
Sans contexte, je ne peux pas savoir exactement ce que vous faites, mais que se passe-t-il lorsque vous rencontrez un tableau partiellement rempli?
Chris Cudmore
6
Soit dit en passant, 2 millions de visites par mois n'ont rien d'effrayant. C'est moins d'un coup par seconde en moyenne.
Mehrdad Afshari
37
Remarque importante : cette question a été fusionnée hier avec une question totalement indépendante sur le fait d'être obligé d'utiliser foreachau lieu de forC #. Si vous voyez ici des réponses qui n'ont aucun sens, c'est pourquoi. Blâmez le modérateur, pas les réponses malheureuses.
TED
7
@TED ​​Oh, je me demandais d'où venaient tous les commentaires "ton patron est un idiot", merci
Gaspa79

Réponses:

350

Patrick Smacchia a blogué à ce sujet le mois dernier, avec les conclusions suivantes:

  • les boucles sur List sont un peu plus de 2 fois moins chères que les boucles foreach sur List.
  • Le bouclage sur la baie est environ 2 fois moins cher que le bouclage sur List.
  • Par conséquent, boucler sur un tableau en utilisant for est 5 fois moins cher que boucler sur List en utilisant foreach (ce que je crois, c'est ce que nous faisons tous).
Ian Nelson
la source
130
Cependant, n'oubliez jamais: "L'optimisation prématurée est la racine de tout mal."
Oorang
18
@Hardwareguy: Une fois que vous savez que for est presque imperceptiblement plus rapide, pourquoi ne devriez-vous pas commencer à l'utiliser en général? Cela ne prend pas de temps supplémentaire.
DevinB
47
@devinb, l'utilisation de "for" est plus difficile que d'utiliser "foreach" car elle ajoute du code, une autre variable, une condition que vous devez vérifier, etc. Combien de fois avez-vous vu une erreur de coupure dans la boucle "foreach" ?
tster
35
@Hardwareguy, laissez-moi voir si j'ai bien compris. Il faut 5 fois plus de temps pour parcourir une liste avec foreachque pour parcourir un tableau avec for, et vous appelez cela insignifiant? Ce genre de différence de performances peut être important pour votre application, et ce n'est peut-être pas le cas, mais je ne voudrais pas simplement le rejeter d'emblée.
Robert Harvey
44
En lisant le billet de blog, il semble que les tests aient été exécutés dans Debug et non Release, ce qui pourrait avoir un facteur. De plus, la différence concerne spécifiquement la surcharge de boucle. Cela n'affecte pas du tout le temps d'exécution du corps de la boucle, ce qui, dans la plupart des cas, est beaucoup plus long que le temps nécessaire pour passer à l'élément suivant de la liste. Ce sont de bonnes informations à savoir lorsque vous avez identifié qu'il y a clairement un problème et que vous avez mesuré la différence dans votre application spécifiquement et qu'il y a une amélioration notable mais certainement pas un conseil général pour supprimer tous les foreachs.
Davy8
164

Tout d'abord, une demande reconventionnelle à la réponse de Dmitry (maintenant supprimée) . Pour les tableaux, le compilateur C # émet en grande partie le même code foreachque pour une forboucle équivalente . Cela explique pourquoi pour ce benchmark, les résultats sont fondamentalement les mêmes:

using System;
using System.Diagnostics;
using System.Linq;

class Test
{
    const int Size = 1000000;
    const int Iterations = 10000;

    static void Main()
    {
        double[] data = new double[Size];
        Random rng = new Random();
        for (int i=0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }

        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j=0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i=0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum-correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop: {0}", sw.ElapsedMilliseconds);
    }
}

Résultats:

For loop: 16638
Foreach loop: 16529

Ensuite, vérifiez que le point de Greg sur le type de collection est important - changez le tableau en a List<double>dans ce qui précède, et vous obtenez des résultats radicalement différents. Non seulement il est considérablement plus lent en général, mais foreach devient beaucoup plus lent que l'accès par index. Cela dit, je préférerais presque toujours foreach à une boucle for où cela rend le code plus simple - car la lisibilité est presque toujours importante, alors que la micro-optimisation l'est rarement.

Jon Skeet
la source
"changez le tableau en List <double> dans ce qui précède, et vous obtenez des résultats radicalement différents" Très intéressant, je n'y avais pas pensé
johnc
5
Compte tenu des étranges différences de résultats entre mes tests et les benchmarks des autres, je pense que cela va mériter un article de blog ...
Jon Skeet
1
Lequel préférez-vous presque toujours entre les tableaux et List<T>? La lisibilité l'emporte-t-elle également sur la micro-optimisation?
JohnB
12
@JohnB: Ouaip - Je préfère presque toujours les List<T>tableaux. Les exceptions sont char[]et byte[]qui sont plus souvent traitées comme des «morceaux» de données plutôt que comme des collections normales.
Jon Skeet
Incroyable, j'obtiens une différence encore plus agressive sur ma machine, près de 10% en faveur de foreach sur des tableaux simples. Je suppose que tout cela découle de la gigue, ne pas avoir à se soucier de la variable supplémentaire, des vérifications liées, etc. serait très intéressant si quelqu'un a une explication approfondie de cela.
Tamir Daniely du
162

foreachles boucles démontrent une intention plus spécifique que les forboucles .

L'utilisation d'une foreachboucle montre à toute personne utilisant votre code que vous prévoyez de faire quelque chose à chaque membre d'une collection, quelle que soit sa place dans la collection. Il montre également que vous ne modifiez pas la collection d'origine (et lève une exception si vous essayez de le faire).

L'autre avantage de cela foreachest qu'il fonctionne sur n'importe quel IEnumerable, où cela forn'a de sens que pour IList, où chaque élément a en fait un index.

Cependant, si vous devez utiliser l'index d'un élément, vous devez bien sûr être autorisé à utiliser une forboucle. Mais si vous n'avez pas besoin d'utiliser un index, en avoir un ne fait qu'encombrer votre code.

Pour autant que je sache, il n'y a aucune incidence significative sur les performances. À un certain moment dans le futur, il pourrait être plus facile d'adapter le code à l'aide foreachde plusieurs cœurs, mais ce n'est pas quelque chose à craindre pour le moment.

ctford
la source
23
ctford: Non, ce n'est pas le cas. Le compilateur ne peut certainement pas réorganiser les éléments dans foreach. foreachn'est pas du tout lié à la programmation fonctionnelle. C'est totalement un paradigme impératif de la programmation. Vous attribuez à tort des choses qui se produisent dans TPL et PLINQ foreach.
Mehrdad Afshari
13
@BlueTrin: Cela garantit certainement l'ordre (la section 8.8.4 de la spécification C # définit formellement l' foreachéquivalent d'une whileboucle). Je pense que je sais que @ctford fait référence. La bibliothèque parallèle de tâches permet à la collection sous-jacente de fournir des éléments dans un ordre arbitraire (en faisant appel .AsParallelà un énumérable). foreachne fait rien ici et le corps de la boucle est exécuté sur un seul thread . La seule chose qui est parallélisée est la génération de la séquence.
Mehrdad Afshari
6
Enumerable.Select a une surcharge qui vous permet d'obtenir l'index de l'élément, donc même le besoin d'un index ne rend pas obligatoire l'utilisation de for. Voir msdn.microsoft.com/en-us/library/bb534869.aspx
TrueWill
5
ForEach est pratique pour la lisibilité et la sauvegarde de la frappe. Les coûts comptent cependant; changer 2 ou 3 boucles for dans une interface utilisateur de concepteur de document que j'ai faite, de (pour chaque obj dans la liste) à (pour i = 0 à list.count-1) a réduit le temps de réponse de 2-3 secondes par modification à environ. 5 sec par édition sur un petit document en boucle à travers quelques centaines d'objets. Maintenant, même pour les documents volumineux, il n'y a pas d'augmentation du temps pour boucler tout. Je ne sais pas comment cela s'est produit. Ce que je sais, c'est que l'alternative était un schéma compliqué pour boucler uniquement un sous-ensemble des objets. Je prendrai le changement de 5 minutes n'importe quel jour! - pas de micro-optimisation.
FastAl
5
@FastAl La différence entre foreachet les forperformances pour les listes normales est de quelques fractions de seconde pour itérer sur des millions d'éléments, de sorte que votre problème n'était certainement pas directement lié aux performances foreach, du moins pas pour quelques centaines d'objets. Cela ressemble à une implémentation d'énumérateur cassée dans la liste que vous utilisiez.
Mike Marynowski
53

Chaque fois qu'il y a des arguments sur les performances, il vous suffit d'écrire un petit test afin que vous puissiez utiliser des résultats quantitatifs pour soutenir votre cas.

Utilisez la classe StopWatch et répétez quelque chose plusieurs millions de fois, pour plus de précision. (Cela pourrait être difficile sans une boucle for):

using System.Diagnostics;
//...
Stopwatch sw = new Stopwatch()
sw.Start()
for(int i = 0; i < 1000000;i ++)
{
    //do whatever it is you need to time
}
sw.Stop();
//print out sw.ElapsedMilliseconds

Les doigts ont croisé les résultats de cette démonstration que la différence est négligeable, et vous pourriez tout aussi bien faire tout ce qui se traduit par le code le plus maintenable

Rob Fonseca-Ensor
la source
13
Mais vous ne pouvez pas comparer les performances de for et foreach. Ils sont censés être utilisés dans différentes circonstances.
Michael Krelin - hacker
7
Je suis d'accord avec toi Michael, tu ne devrais pas choisir lequel utiliser en fonction des performances - tu devrais choisir celui qui a le plus de sens! Mais si votre patron dit "Ne pas utiliser pour car c'est plus lent que pour foreach", alors c'est le seul moyen de le convaincre que la différence est négligeable
Rob Fonseca-Ensor
"(Cela peut être difficile sans une boucle for)" Ou vous pouvez utiliser une boucle while.
jonescb
49

Ce sera toujours proche. Pour un tableau, c'est parfois un for peu plus rapide, mais il foreachest plus expressif et offre LINQ, etc. En général, respectez foreach.

En outre, foreachpeut être optimisé dans certains scénarios. Par exemple, une liste chaînée peut être terrible par indexeur, mais elle peut être rapide par foreach. En fait, la norme LinkedList<T>n'offre même pas d'indexeur pour cette raison.

Marc Gravell
la source
Alors, vous dites que LinkedList<T>c'est plus maigre que List<T>? Et si je vais toujours utiliser foreach(au lieu de for), je ferais mieux d'utiliser LinkedList<T>?
JohnB
2
@JohnB - pas plus maigre; juste différent. Par exemple, chaque nœud d'une liste chaînée a des références supplémentaires qui ne sont pas nécessaires pour un tableau plat (qui sous-tend également List<T>). C'est plus qu'il est moins cher à insérer / retirer .
Marc Gravell
36

Je suppose que ce ne sera probablement pas significatif dans 99% des cas, alors pourquoi choisiriez-vous le plus rapide au lieu du plus approprié (comme dans le plus facile à comprendre / à maintenir)?

Brian Rasmussen
la source
6
@klew, Si vous profilez réellement votre code, vous n'aurez pas à deviner quels 20% doivent être aussi rapides que possible. Vous découvrirez probablement également que le nombre réel de boucles qui doivent être rapides est beaucoup plus faible. De plus, dites-vous vraiment que l'acte de boucler est l'endroit où vous passez votre temps, par opposition à ce que vous faites réellement dans cette boucle?
tster
32

Il est peu probable qu'il y ait une énorme différence de performances entre les deux. Comme toujours, face à un "qui est plus rapide?" question, vous devez toujours penser "je peux mesurer cela."

Écrivez deux boucles qui font la même chose dans le corps de la boucle, exécutez-les et chronométrez-les toutes les deux, et voyez quelle est la différence de vitesse. Faites-le avec un corps presque vide et un corps de boucle similaire à ce que vous allez réellement faire. Essayez-le également avec le type de collection que vous utilisez, car différents types de collections peuvent avoir des caractéristiques de performances différentes.

Greg Hewgill
la source
32

Il y a de très bonnes raisons de préférer les foreach boucles aux forboucles. Si vous pouvez utiliser une foreachboucle, votre patron a raison.

Cependant, chaque itération ne passe pas simplement par une liste dans l'ordre un par un. S'il interdit , oui, c'est faux.

Si j'étais vous, ce que je ferais, c'est transformer toutes vos boucles naturelles en récursivité . Cela lui apprendrait, et c'est aussi un bon exercice mental pour vous.

TED
la source
Comment la récursivité se compare-t-elle aux forboucles et aux boucles en foreachtermes de performances?
JohnB
Ça dépend. Si vous utilisez la récursion de queue et que votre compilateur est suffisamment intelligent pour le remarquer, il peut être identique. OTOH: Si ce n'est pas le cas et que vous faites quelque chose de stupide comme passer beaucoup de données inutiles (immuables) en tant que paramètres ou déclarer de grandes structures sur la pile en tant que paramètres locaux, cela peut être vraiment très lent (ou même manquer de RAM).
TED
Ahhh. Je vois pourquoi tu as demandé ça maintenant. Cette réponse est allée à une question totalement différente. Pour une raison bizarre, Jonathan Sampson a fusionné les deux hier. Il n'aurait vraiment pas dû faire ça. Les réponses fusionnées n'auront aucun sens ici.
TED
18

Jeffrey Richter sur TechEd 2005:

"J'ai appris au fil des ans que le compilateur C # est fondamentalement un menteur pour moi." .. "Cela repose sur beaucoup de choses." .. "Comme quand vous faites une boucle foreach ..." .. "... c'est une petite ligne de code que vous écrivez, mais ce que le compilateur C # crache pour le faire est phénoménal. Il met un essayez / finalement bloquer là-dedans, à l'intérieur du bloc finally, il convertit votre variable en une interface IDisposable, et si le transtypage réussit, il appelle la méthode Dispose dessus, à l'intérieur de la boucle, il appelle la propriété Current et la méthode MoveNext à plusieurs reprises à l'intérieur de la boucle, des objets sont créés sous les couvertures. Beaucoup de gens utilisent foreach parce que c'est très facile à coder, très facile à faire .. ".." foreach n'est pas très bon en termes de performances,

Webdiffusion à la demande: http://msevents.microsoft.com/CUI/WebCastEventDetails.aspx?EventID=1032292286&EventCategory=3&culture=en-US&CountryCode=US

Max Toro
la source
12

C'est ridicule. Il n'y a aucune raison impérieuse d'interdire la boucle for, en termes de performances ou autre.

Voir le blog de Jon Skeet pour une référence de performance et d'autres arguments.

Rik
la source
2
Lien mis à jour: codeblog.jonskeet.uk/2009/01/29/…
Matt
La construction de boucle qui est plus rapide dépend de ce que vous devez parcourir. Un autre blog qui compare plusieurs itérations sur plusieurs types d'objets , tels que DataRows et des objets personnalisés. Il inclut également les performances de la construction de boucle While et pas seulement les constructions de boucle for et foreach.
Free Coder 24
11

Dans les cas où vous travaillez avec une collection d'objets, foreachc'est mieux, mais si vous incrémentez un nombre, une forboucle est meilleure.

Notez que dans le dernier cas, vous pourriez faire quelque chose comme:

foreach (int i in Enumerable.Range(1, 10))...

Mais il ne fonctionne certainement pas mieux, il a en fait des performances inférieures à celles d'un for.

Meta-Knight
la source
"Mieux" est discutable: il est plus lent et le débogueur dnspy ​​ne s'introduira pas dans un foreach C # (bien que le débogueur VS2017 le fasse). Parfois plus lisible, mais si vous prenez en charge des langues sans, cela peut être ennuyeux.
Zeek2
10

Cela devrait vous faire économiser:

public IEnumerator<int> For(int start, int end, int step) {
    int n = start;
    while (n <= end) {
        yield n;
        n += step;
    }
}

Utilisation:

foreach (int n in For(1, 200, 4)) {
    Console.WriteLine(n);
}

Pour une plus grande victoire, vous pouvez prendre trois délégués comme paramètres.

akuhn
la source
1
Une petite différence est qu'une forboucle est généralement écrite pour exclure la fin de la plage (par exemple 0 <= i < 10). Parallel.Forfait également cela pour le garder facilement interchangeable avec une forboucle commune .
Groo
9

vous pouvez en lire plus dans Deep .NET - partie 1 Itération

il couvre les résultats (sans la première initialisation) du code source .NET jusqu'au démontage.

par exemple - Array Iteration avec une boucle foreach: entrez la description de l'image ici

et - lister l'itération avec la boucle foreach: entrez la description de l'image ici

et les résultats finaux: entrez la description de l'image ici

entrez la description de l'image ici

ou Yaacov
la source
8

Les différences de vitesse dans une boucle for- et une foreach- sont minuscules lorsque vous parcourez des structures courantes comme des tableaux, des listes, etc., et faire une LINQrequête sur la collection est presque toujours un peu plus lent, bien que ce soit plus agréable à écrire! Comme les autres affiches l'ont dit, optez pour l'expressivité plutôt qu'une milliseconde de performances supplémentaires.

Ce qui n'a pas été dit jusqu'à présent, c'est que lorsqu'une foreachboucle est compilée, elle est optimisée par le compilateur en fonction de la collection sur laquelle elle itère. Cela signifie que lorsque vous n'êtes pas sûr de la boucle à utiliser, vous devez utiliser la foreachboucle - elle générera la meilleure boucle pour vous lorsqu'elle sera compilée. C'est aussi plus lisible.

Un autre avantage clé de la foreachboucle est que si votre implémentation de collection change (d'un int arrayà un List<int>par exemple), votre foreachboucle ne nécessitera aucune modification de code:

foreach (int i in myCollection)

Ce qui précède est le même quel que soit le type de votre collection, alors que dans votre forboucle, ce qui suit ne sera pas construit si vous êtes passé myCollectionde an arrayà a List:

for (int i = 0; i < myCollection.Length, i++)
Alex York
la source
7

"Y a-t-il des arguments que je pourrais utiliser pour m'aider à le convaincre que la boucle for est acceptable?"

Non, si votre patron fait de la microgestion au point de vous dire quels concepts de langage de programmation utiliser, il n'y a vraiment rien à dire. Désolé.

Ken
la source
7

Cela dépend probablement du type de collection que vous énumérez et de l'implémentation de son indexeur. En général cependant, l'utilisation foreachest probablement une meilleure approche.

En outre, cela fonctionnera avec tout IEnumerable- pas seulement avec les indexeurs.

Andrew Kennan
la source
7

Cela a les deux mêmes réponses que la plupart des questions "ce qui est plus rapide":

1) Si vous ne mesurez pas, vous ne savez pas.

2) (Parce que ...) Cela dépend.

Cela dépend du coût de la méthode "MoveNext ()", par rapport au coût de la méthode "this [int index]", pour le type (ou les types) de IEnumerable que vous allez parcourir.

Le mot clé "foreach" est un raccourci pour une série d'opérations - il appelle GetEnumerator () une fois sur l'IEnumerable, il appelle MoveNext () une fois par itération, il effectue une vérification de type, etc. La chose la plus susceptible d'avoir un impact sur les mesures de performances est le coût de MoveNext () car il est invoqué O (N) fois. C'est peut-être bon marché, mais peut-être pas.

Le mot clé "for" semble plus prévisible, mais dans la plupart des boucles "for", vous trouverez quelque chose comme "collection [index]". Cela ressemble à une opération d'indexation de tableau simple, mais il s'agit en fait d'un appel de méthode, dont le coût dépend entièrement de la nature de la collection sur laquelle vous effectuez l'itération. C'est probablement bon marché, mais ce n'est peut-être pas le cas.

Si la structure sous-jacente de la collection est essentiellement une liste chaînée, MoveNext est bon marché, mais l'indexeur peut avoir un coût O (N), ce qui rend le coût réel d'une boucle "pour" O (N * N).

NSFW
la source
6

Chaque construction de langage a une heure et un lieu appropriés pour son utilisation. Il y a une raison pour laquelle le langage C # a quatre instructions d'itération distinctes - chacune est là pour un objectif spécifique et a une utilisation appropriée.

Je recommande de vous asseoir avec votre patron et d'essayer d'expliquer rationnellement pourquoi une forboucle a un but. Parfois, un forbloc d'itération décrit plus clairement un algorithme qu'une foreachitération. Lorsque cela est vrai, il convient de les utiliser.

Je voudrais également signaler à votre patron - La performance n'est pas, et ne devrait pas être un problème de quelque manière pratique - c'est plus une question d'expression de l'algorithme d'une manière succincte, significative et maintenable. Les micro-optimisations comme celle-ci manquent complètement l'optimisation des performances, car tout avantage réel en termes de performances proviendra d'une refonte et d'une refactorisation algorithmiques, et non d'une restructuration de boucle.

Si, après une discussion rationnelle, il y a toujours ce point de vue autoritaire, c'est à vous de décider comment procéder. Personnellement, je ne serais pas heureux de travailler dans un environnement où la pensée rationnelle est découragée et envisagerais de passer à un autre poste sous un autre employeur. Cependant, je recommande fortement la discussion avant de s'énerver - il peut simplement y avoir un simple malentendu.

Reed Copsey
la source
5

C'est ce que vous faites à l' intérieur de la boucle qui affecte les performances, pas la construction de boucle réelle (en supposant que votre cas n'est pas trivial).

Martin Wickman
la source
5

Que ce forsoit plus rapide que ce qui foreachest vraiment hors de propos. Je doute fortement que choisir l'un plutôt que l'autre ait un impact significatif sur vos performances.

La meilleure façon d'optimiser votre application consiste à profiler le code réel. Cela identifiera les méthodes qui représentent le plus de travail / temps. Optimisez-les d'abord. Si les performances ne sont toujours pas acceptables, répétez la procédure.

En règle générale, je recommanderais de rester à l'écart des micro-optimisations car elles donneront rarement des gains significatifs. La seule exception est lors de l'optimisation des chemins d'accès identifiés (c.-à-d. Si votre profilage identifie quelques méthodes très utilisées, il peut être judicieux de les optimiser largement).

Brian Rasmussen
la source
Si le seul type d'optimisation que je devais faire dans les projets sur lesquels je travaillais était des micro-optimisations, je serais un campeur heureux. Malheureusement, ce n'est jamais le cas.
Yannick Motton,
2
forest légèrement plus rapide que foreach. Je m'opposerais sérieusement à cette déclaration. Cela dépend totalement de la collection sous-jacente. Si une classe de liste liée fournit un indexeur avec un paramètre entier, je m'attendrais à ce qu'une forboucle soit O (n ^ 2) alors qu'elle foreachdevrait être O (n).
Mehrdad Afshari
@Merhdad: En fait, c'est un bon point. Je pensais juste au cas normal d'indexation d'une liste (ie tableau). Je vais reformuler pour refléter cela. Merci.
Brian Rasmussen
@Mehrdad Afshari: L'indexation d'une collection par entier peut être beaucoup plus lente que son énumération. Mais vous comparez en fait l'utilisation for et la recherche d'un indexeur à l'utilisation foreachseule. Je pense que la réponse de @Brian Rasmussen est correcte, à part toute utilisation avec une collection, forsera toujours légèrement plus rapide que foreach. Cependant, forplus une recherche de collection sera toujours plus lente que foreachpar elle-même.
Daniel Pryden
@Daniel: Soit vous avez un tableau simple, pour lequel les deux vont générer du code identique, soit un indexeur est impliqué lorsque vous utilisez l' forinstruction. La forboucle simple avec une variable de contrôle entière n'est pas comparable à foreach, donc c'est fini. Je comprends ce que @Brian signifie et c'est correct comme vous le dites, mais la réponse peut être trompeuse. Re: Votre dernier point: non, en fait, forplus List<T>est encore plus rapide que foreach.
Mehrdad Afshari
4

Les deux fonctionneront presque exactement de la même manière. Écrivez du code pour utiliser les deux, puis montrez-lui l'IL. Il doit montrer des calculs comparables, ce qui signifie aucune différence de performances.

cjk
la source
Le compilateur reconnaît les boucles foreach utilisées sur les tableaux / ILists, etc. et les transforme en boucles for.
Callum Rogers
3
Montrez-lui des lignes de preuve inintelligibles que c'est OK et demandez-lui sa preuve que ce n'est pas OK.
cjk
3

for a une logique plus simple à implémenter, donc c'est plus rapide que foreach.

Tarik
la source
3

À moins que vous ne soyez dans un processus d'optimisation de vitesse spécifique, je dirais utiliser la méthode qui produit le code le plus facile à lire et à maintenir.

Si un itérateur est déjà configuré, comme avec l'une des classes de collection, alors foreach est une bonne option facile. Et si c'est une plage entière que vous itérez, alors c'est probablement plus propre.

GeekyMonkey
la source
3

Dans la plupart des cas, il n'y a vraiment aucune différence.

En règle générale, vous devez toujours utiliser foreach lorsque vous n'avez pas d'index numérique explicite, et vous devez toujours utiliser for lorsque vous n'avez pas réellement de collection itérable (par exemple, itérer sur une grille de tableau à deux dimensions dans un triangle supérieur) . Dans certains cas, vous avez le choix.

On pourrait faire valoir que pour les boucles peut être un peu plus difficile à maintenir si des nombres magiques commencent à apparaître dans le code. Vous devriez avoir raison d'être ennuyé de ne pas pouvoir utiliser une boucle for et d'avoir à créer une collection ou à utiliser un lambda pour construire une sous-collection à la place simplement parce que les boucles for ont été interdites.

Cade Roux
la source
3

Il semble un peu étrange d'interdire totalement l'utilisation de quelque chose comme une boucle for.

Il y a un article intéressant ici qui couvre beaucoup de différences de performances entre les deux boucles.

Je dirais personnellement que je trouve foreach un peu plus lisible pour les boucles, mais vous devriez utiliser le meilleur pour le travail à effectuer et ne pas avoir à écrire du code extra long pour inclure une boucle foreach si une boucle for est plus appropriée.

colethecoder
la source
Citation essentielle de l'article auquel vous liez: "... si vous prévoyez d'écrire du code haute performance qui n'est pas pour les collections, utilisez pour la boucle. Même pour les collections, foreach peut sembler pratique lors de l'utilisation, mais ce n'est pas si efficace."
NickFitz
3

J'ai trouvé la foreachboucle qui itère List plus rapidement . Voir mes résultats de test ci-dessous. Dans le code ci-dessous, j'itère une arraytaille 100, 10000 et 100000 séparément en utilisant foret en foreachboucle pour mesurer le temps.

entrez la description de l'image ici

private static void MeasureTime()
    {
        var array = new int[10000];
        var list = array.ToList();
        Console.WriteLine("Array size: {0}", array.Length);

        Console.WriteLine("Array For loop ......");
        var stopWatch = Stopwatch.StartNew();
        for (int i = 0; i < array.Length; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("Array Foreach loop ......");
        var stopWatch1 = Stopwatch.StartNew();
        foreach (var item in array)
        {
            Thread.Sleep(1);
        }
        stopWatch1.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch1.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List For loop ......");
        var stopWatch2 = Stopwatch.StartNew();
        for (int i = 0; i < list.Count; i++)
        {
            Thread.Sleep(1);
        }
        stopWatch2.Stop();
        Console.WriteLine("Time take to run the for loop is {0} millisecond", stopWatch2.ElapsedMilliseconds);

        Console.WriteLine(" ");
        Console.WriteLine("List Foreach loop ......");
        var stopWatch3 = Stopwatch.StartNew();
        foreach (var item in list)
        {
            Thread.Sleep(1);
        }
        stopWatch3.Stop();
        Console.WriteLine("Time take to run the foreach loop is {0} millisecond", stopWatch3.ElapsedMilliseconds);
    }

MISE À JOUR

Après la suggestion de @jgauffin, j'ai utilisé le code @johnskeet et constaté que la forboucle avec arrayest plus rapide que la suivante,

  • Boucle foreach avec tableau.
  • Pour boucle avec liste.
  • Boucle foreach avec liste.

Voir mes résultats de test et mon code ci-dessous,

entrez la description de l'image ici

private static void MeasureNewTime()
    {
        var data = new double[Size];
        var rng = new Random();
        for (int i = 0; i < data.Length; i++)
        {
            data[i] = rng.NextDouble();
        }
        Console.WriteLine("Lenght of array: {0}", data.Length);
        Console.WriteLine("No. of iteration: {0}", Iterations);
        Console.WriteLine(" ");
        double correctSum = data.Sum();

        Stopwatch sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < data.Length; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with Array: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (var i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in data)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with Array: {0}", sw.ElapsedMilliseconds);
        Console.WriteLine(" ");

        var dataList = data.ToList();
        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            for (int j = 0; j < dataList.Count; j++)
            {
                sum += data[j];
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("For loop with List: {0}", sw.ElapsedMilliseconds);

        sw = Stopwatch.StartNew();
        for (int i = 0; i < Iterations; i++)
        {
            double sum = 0;
            foreach (double d in dataList)
            {
                sum += d;
            }
            if (Math.Abs(sum - correctSum) > 0.1)
            {
                Console.WriteLine("Summation failed");
                return;
            }
        }
        sw.Stop();
        Console.WriteLine("Foreach loop with List: {0}", sw.ElapsedMilliseconds);
    }
Diganta Kumar
la source
3
C'est un très mauvais test. a) vous faites trop peu d'itérations pour obtenir une réponse concluante b) Ce Thread.Sleep n'attendra pas vraiment une milliseconde. Utilisez la même méthode que Jon Skeet dans sa réponse.
jgauffin
1
99,99% du temps est définitivement passé dans le thread.sleep (ce qui ne garantit pas la vitesse à laquelle il reviendra, sauf qu'il ne le sera pas avant au moins ce temps). La boucle est très rapide et le sommeil est très lent, vous n'utilisez pas la dernière pour tester la première.
Ronan Thibaudau
3

Vous pouvez vraiment visser avec sa tête et opter pour une fermeture IQueryable .foreach à la place:

myList.ForEach(c => Console.WriteLine(c.ToString());
Tad Donaghe
la source
3
Je remplacerais votre ligne de code par myList.ForEach(Console.WriteLine).
Mehrdad Afshari
2

Je ne m'attendrais pas à ce que quelqu'un trouve une "énorme" différence de performances entre les deux.

Je suppose que la réponse dépend du fait que la collection à laquelle vous essayez d'accéder ait une implémentation d'accès indexeur plus rapide ou une implémentation d'accès IEnumerator plus rapide. Étant donné que IEnumerator utilise souvent l'indexeur et ne contient qu'une copie de la position d'index actuelle, je m'attends à ce que l'accès de l'énumérateur soit au moins aussi lent ou plus lent que l'accès direct à l'index, mais pas de beaucoup.

Bien sûr, cette réponse ne tient pas compte des optimisations que le compilateur peut implémenter.

JohannesH
la source
Le compilateur C # fait très peu d'optimisation, il laisse vraiment cela au JITter.
ljs
Eh bien, le JITter est un compilateur ... D'accord?
JohannesH
2

Gardez à l'esprit que la boucle for et la boucle foreach ne sont pas toujours équivalentes. Les énumérateurs de liste lèveront une exception si la liste change, mais vous n'obtiendrez pas toujours cet avertissement avec une boucle for normale. Vous pourriez même obtenir une exception différente si la liste change au mauvais moment.

Craig Gidney
la source
Si la liste change sous vous, vous ne pouvez pas compter sur l'énumérateur qui soutient une boucle foreach pour vous le dire. Il ne va pas vérifier à nouveau après avoir renvoyé la valeur, ce qui entraîne une course.
hoodaticus