c# while Dans.NET, quelle boucle s'exécute plus vite, 'for' ou 'foreach'?




parallel foreach c# (24)

It seems a bit strange to totally forbid the use of something like a for loop.

There's an interesting article here that covers a lot of the performance differences between the two loops.

I would say personally I find foreach a bit more readable over for loops but you should use the best for the job at hand and not have to write extra long code to include a foreach loop if a for loop is more appropriate.

En C # / VB.NET / .NET, quelle boucle s'exécute plus vite, for ou foreach ?

Depuis que j'ai lu qu'une boucle for fonctionne plus vite qu'une boucle foreach a longtemps, j'ai supposé qu'elle était vraie pour toutes les collections, 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.

Ce qui serait idéal est d'avoir chaque scénario répertorié et la meilleure solution pour le même.

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

  1. pour l'itération d'un tableau de plus de 1000 chaînes - for vaut mieux que pour foreach
  2. pour itérer sur les chaînes IList (non génériques) - foreach est meilleur que for

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

  1. Original grand vieux article par Emmanuel Schanzer
  2. CodeProject Vs FOREACH POUR
  3. Blog - Pour foreach ou pas foreach , c'est la question
  4. Forum ASP.NET - NET 1.1 C # for vs foreach

[Modifier]

En dehors de l'aspect de lisibilité, je m'intéresse vraiment aux faits et aux chiffres. Il y a des applications où le dernier kilomètre de l'optimisation des performances pressé est important.


D'abord, une demande reconventionnelle à la réponse de Dmitry . Pour les tableaux, le compilateur C # émet en grande partie le même code pour foreach que pour un équivalent for loop. 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, validation que le point de Greg concernant le type de collection est important - changez le tableau en List<double> dans ce qui précède, et vous obtenez des résultats radicalement différents. Non seulement il est significativement plus lent en général, mais foreach devient significativement plus lent que l'accès par index. Cela étant dit, je préférerais presque toujours que chaque boucle soit rendue plus simple, car la lisibilité est presque toujours importante, alors que la micro-optimisation l'est rarement.


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 de foreach est susceptible d'être une meilleure approche.

En outre, il fonctionnera avec n'importe quel IEnumerable - pas simplement des choses avec des indexeurs.


car a une logique plus simple à mettre en œuvre, c'est donc plus rapide que foreach.


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


C'est ridicule. Il n'y a pas de raison impérieuse d'interdire la boucle forcée, la performance ou autre.

Voir le blog de Jon Skeet pour un benchmark de performance et d'autres arguments.


Dans les cas où vous travaillez avec une collection d'objets, foreach est meilleur, mais si vous incrémentez un nombre, une boucle for 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 moins bonnes par rapport à un.


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

Je recommande de s'asseoir avec votre patron et d'essayer d'expliquer rationnellement pourquoi une boucle for a un but. Il y a des moments où un bloc d'itération décrit plus clairement un algorithme qu'une itération foreach . Lorsque cela est vrai, il est approprié de les utiliser.

Je signale également à votre patron - Performance n'est pas, et ne devrait pas être un problème d'une 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 le point de l'optimisation des performances, car tout réel bénéfice de performance viendra de la refonte et de la refonte algorithmiques, et non de la restructuration en boucle.

Si, après une discussion rationnelle, il y a toujours cette vision 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 employeur différent. Cependant, je recommande fortement de discuter avant de me fâcher - il peut y avoir un simple malentendu en place.


Keep in mind that the for-loop and foreach-loop are not always equivalent. List enumerators will throw an exception if the list changes, but you won't always get that warning with a normal for loop. You might even get a different exception if the list changes at just the wrong time.


I wouldn't expect anyone to find a "huge" performance difference between the two.

I guess the answer depends on the whether the collection you are trying to access has a faster indexer access implementation or a faster IEnumerator access implementation. Since IEnumerator often uses the indexer and just holds a copy of the current index position, I would expect enumerator access to be at least as slow or slower than direct index access, but not by much.

Of course this answer doesn't account for any optimizations the compiler may implement.


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 de la cherté 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 IEnumerable, il appelle MoveNext () une fois par itération, il vérifie le type, et ainsi de suite. La chose la plus susceptible d'avoir un impact sur les mesures de performance est le coût de MoveNext () puisque ce dernier est invoqué O (N) fois. Peut-être que c'est bon marché, mais peut-être que ce n'est pas le cas.

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 simple opération d'indexation de tableau, 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 que vous itérez. Probablement c'est pas cher, mais peut-être pas.

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


foreach boucles foreach démontrent une intention plus spécifique que for boucles .

L'utilisation d'une boucle foreach démontre à quiconque utilise votre code que vous prévoyez de faire quelque chose pour 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 foreach est que cela fonctionne sur n'importe quel IEnumerable , où cela n'a de sens que pour IList , où chaque élément a réellement un index.

Cependant, si vous avez besoin d'utiliser l'index d'un élément, vous devriez bien sûr pouvoir utiliser une boucle for . Mais si vous n'avez pas besoin d'utiliser un index, en avoir un est juste encombrer votre code.

Pour autant que je sache, il n'y a pas d'implications significatives en termes de performances. À un certain moment dans le futur, il pourrait être plus facile d'adapter le code en utilisant foreach pour exécuter sur plusieurs cœurs, mais ce n'est pas quelque chose à craindre pour le moment.


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

Ecrire deux boucles qui font la même chose dans le corps de la boucle, les exécuter et les chronométrer toutes les deux, et voir quelle est la différence de vitesse. Pour ce faire, avec un corps presque vide, et un corps en 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.


Cela devrait vous sauver:

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 gagner plus, vous pouvez prendre trois délégués comme paramètres.



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

Utilisez la classe StopWatch et répétez quelque chose quelques 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 croisés les résultats de cette émission que la différence est négligeable, et vous pourriez aussi bien faire tout ce qui résulte dans le code le plus maintenable


Sauf si vous êtes dans un processus d'optimisation de vitesse spécifique, je dirais utiliser n'importe quelle méthode 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 le foreach est une bonne option facile. Et si c'est un nombre entier, vous itérez, alors c'est probablement plus propre.


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

  • pour les boucles sur la liste sont un peu plus de 2 fois moins cher que les boucles foreach sur la liste.
  • Le bouclage sur le tableau est environ 2 fois moins cher que le bouclage sur List.
  • Par conséquent, boucler sur array en utilisant pour est 5 fois moins cher que boucler sur List en utilisant foreach (ce que je crois, c'est ce que nous faisons tous).

Jeffrey Richter sur TechEd 2005:

"Je suis venu apprendre au fil des années que le compilateur C # est fondamentalement un menteur pour moi." .. "Il ment 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 faire ça c'est phénoménal. try / finally bloque dedans, à l'intérieur du bloc finally, il place votre variable dans une interface IDisposable, et si la distribution réussit, elle appelle la méthode Dispose, elle appelle la propriété Current et la méthode MoveNext à plusieurs reprises dans la boucle, des objets sont créés sous les couvertures.Certains 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 performance, si vous itérez sur une collection au lieu d'utiliser square la notation des parenthèses, en faisant simplement l'index, c'est beaucoup plus rapide, et cela ne crée aucun objet sur le tas ... "

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


J'ai trouvé la boucle foreach qui itérait à travers une List plus rapidement . Voir mes résultats de test ci-dessous. Dans le code ci-dessous, j'itéère un array de taille 100, 10000 et 100000 séparément en utilisant la boucle for et foreach pour mesurer le temps.

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);
    }

ACTUALISÉ

Après la suggestion de @jgauffin, j'ai utilisé le code @johnskeet et j'ai trouvé que la boucle for avec array est plus rapide que la suivante,

  • Foreach boucle avec tableau.
  • Pour boucle avec liste.
  • Foreach boucle avec liste.

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

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);
    }

Que ce soit for est plus rapide que foreach est vraiment à côté du point. Je doute sérieusement que le choix de l'un sur l'autre aura un impact significatif sur votre performance.

La meilleure façon d'optimiser votre application est de profiler le code réel. Cela permettra d'identifier les méthodes qui représentent le plus de travail / temps. Optimiser les premiers. Si la performance n'est toujours pas acceptable, 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. Seule exception lors de l'optimisation des chemins chauds identifiés (c'est-à-dire si votre profilage identifie quelques méthodes très utilisées, il peut être judicieux de les optimiser largement).


Ce sera toujours proche. Pour un tableau, parfois for est légèrement plus rapide, mais foreach est plus expressif, et offre LINQ, etc. En général, foreach avec foreach .

De plus, foreach peut être optimisé dans certains scénarios. Par exemple, une liste liée peut être terrible par indexeur, mais elle peut être rapide par foreach . En fait, le LinkedList<T> standard LinkedList<T> n'offre même pas d'indexeur pour cette raison.


Il y a de très bonnes raisons de préférer les boucles foreach for boucles. Si vous pouvez utiliser une boucle foreach , votre patron a raison de le faire.

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

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


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







for-loop