while - parallel foreach c#




Dans.NET, quelle boucle s'exécute plus vite, 'for' ou 'foreach'? (20)

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.


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

Non, si votre patron est en train de faire de la microgestion pour vous dire quel langage de programmation utiliser, il n'y a vraiment rien que vous puissiez dire. Pardon.


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).


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.


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).


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 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.


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.


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

Généralement, vous devez toujours utiliser foreach quand vous n'avez pas d'index numérique explicite, et vous devez toujours utiliser quand vous n'avez pas de collection itérable (par exemple itération sur une grille de tableau bidimensionnelle dans un triangle supérieur) . Il y a des cas où vous avez le choix.

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


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.


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.


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)?



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


Les différences de vitesse dans un for - et un foreach -loop sont minuscules quand vous parcourez des structures communes comme des tableaux, des listes, etc., et faire une requête LINQ sur la collection est presque toujours un peu plus lent, bien qu'il soit plus agréable d'écrire! Comme l'ont dit les autres affiches, optez pour l'expressivité plutôt que pour une milliseconde de performance supplémentaire.

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

Un autre avantage clé de la boucle foreach est que si votre implémentation de collection change (d'un array int en un List<int> par exemple) alors votre boucle foreach 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 boucle for , les éléments suivants ne se construisent pas si vous avez changé myCollection d'un array à un List :

for (int i = 0; i < myCollection.Length, i++)

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).


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.


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.


I did test it a while ago, with the result that a for loop is much faster than a foreach loop. The cause is simple, the foreach loop first needs to instantiate an IEnumerator for the collection.


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.


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.





for-loop