Welche Schleife wird in .NET schneller ausgeführt, 'for' oder 'foreach'?

345

Welche Schleife wird in C # / VB.NET / .NET schneller ausgeführt, foroder foreach?

Seit ich vor langer Zeit gelesen habe, dass eine forSchleife schneller funktioniert als eine foreachSchleife, habe ich angenommen, dass sie für alle Sammlungen, generischen Sammlungen, alle Arrays usw. gilt.

Ich habe Google durchsucht und einige Artikel gefunden, aber die meisten sind nicht schlüssig (Kommentare zu den Artikeln lesen) und offen.

Ideal wäre es, jedes Szenario aufzulisten und die beste Lösung dafür zu finden.

Zum Beispiel (nur ein Beispiel dafür, wie es sein sollte):

  1. zum Iterieren eines Arrays von mehr als 1000 Zeichenfolgen - forist besser alsforeach
  2. zum Iterieren über IList(nicht generische) Zeichenfolgen - foreachist besser alsfor

Einige Referenzen im Internet für das gleiche gefunden:

  1. Original großartiger alter Artikel von Emmanuel Schanzer
  2. CodeProject FOREACH Vs. ZUM
  3. Blog - Zu foreachoder nicht zu foreach, das ist die Frage
  4. ASP.NET-Forum - NET 1.1 C # forvs.foreach

[Bearbeiten]

Abgesehen von dem Aspekt der Lesbarkeit interessiere ich mich sehr für Fakten und Zahlen. Es gibt Anwendungen, bei denen die letzte Meile der Leistungsoptimierung eine Rolle spielt.

Binoj Antony
quelle
3
Der Unterschied besteht immer noch. Insbesondere Arrays sollten unter foreach genauso schnell sein, aber für alles andere sind einfache Loops schneller. Natürlich macht dies die meiste Zeit keinen Unterschied, und natürlich könnte ein cleverer JIT-Compiler den Unterschied theoretisch beseitigen.
Jalf
3
Ohne Kontext kann ich nicht genau wissen, was Sie tun, aber was passiert, wenn Sie auf ein teilweise gefülltes Array stoßen?
Chris Cudmore
6
Übrigens sind 2 Millionen Treffer / Monat nichts Unheimliches. Es ist im Durchschnitt weniger als ein Treffer pro Sekunde.
Mehrdad Afshari
37
Wichtiger Hinweis : Diese Frage wurde gestern mit einer völlig unabhängigen Frage über die Verwendung foreachanstelle von forC # zusammengeführt. Wenn Sie hier Antworten sehen, die überhaupt keinen Sinn ergeben, ist das der Grund. Beschuldigen Sie den Moderator, nicht die unglücklichen Antworten.
Ted
7
@ Ted Oh, ich habe mich gefragt, woher all die Kommentare "Ihr Chef ist ein Idiot" kommen, danke
Gaspa79

Antworten:

350

Patrick Smacchia hat über diesen letzten Monat mit den folgenden Schlussfolgerungen gebloggt :

  • for-Schleifen auf der Liste sind etwas mehr als zweimal billiger als foreach-Schleifen auf der Liste.
  • Das Schleifen auf einem Array ist ungefähr zweimal billiger als das Schleifen auf einer Liste.
  • Infolgedessen ist das Schleifen auf einem Array mit for fünfmal billiger als das Schleifen auf einer Liste mit foreach (was meiner Meinung nach das ist, was wir alle tun).
Ian Nelson
quelle
130
Vergessen Sie jedoch niemals: "Vorzeitige Optimierung ist die Wurzel allen Übels."
Oorang
18
@Hardwareguy: Wenn Sie wissen, dass for fast unmerklich schneller ist, warum sollten Sie es dann nicht generell verwenden? Es dauert keine zusätzliche Zeit.
DevinB
47
@devinb, die Verwendung von "for" ist schwieriger als die Verwendung von "foreach", da Code, eine andere Variable, eine zu überprüfende Bedingung usw. hinzugefügt werden. Wie oft haben Sie in einer "foreach" -Schleife einen Fehler nach dem anderen gesehen? ?
Tster
35
@ Hardwareguy, lass mich sehen, ob ich das klargestellt habe. Das Durchlaufen einer Liste mit dauert fünfmal länger foreachals das Durchlaufen eines Arrays mit for, und Sie nennen das unbedeutend? Diese Art von Leistungsunterschied könnte für Ihre Anwendung von Bedeutung sein und auch nicht, aber ich würde ihn nicht einfach sofort ablehnen.
Robert Harvey
44
Beim Lesen des Blogposts sieht es so aus, als ob die Tests in Debug und nicht in Release ausgeführt wurden, sodass dies möglicherweise einen Faktor hat. Außerdem betrifft der Unterschied nur den Loop-Overhead. Es hat keinen Einfluss auf die Zeit, um den Hauptteil der Schleife auszuführen. In den meisten Fällen ist dies viel länger als die Zeit, die benötigt wird, um zum nächsten Element der Liste zu gelangen. Es ist eine gute Information zu wissen, wenn Sie festgestellt haben, dass es eindeutig ein Problem gibt und Sie den Unterschied in Ihrer App spezifisch gemessen haben und es eine spürbare Verbesserung gibt, aber definitiv keine allgemeinen Ratschläge zum Entfernen aller foreachs.
Davy8
164

Zunächst eine Gegenforderung zu Dmitrys (jetzt gelöschter) Antwort . Bei Arrays gibt der C # -Compiler weitgehend denselben Code aus foreachwie bei einer äquivalenten forSchleife. Das erklärt, warum für diesen Benchmark die Ergebnisse grundsätzlich gleich sind:

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

Ergebnisse:

For loop: 16638
Foreach loop: 16529

Als nächstes überprüfen Sie, ob Gregs Aussage über den Sammlungstyp wichtig ist. Ändern Sie das Array in a List<double>oben, und Sie erhalten radikal unterschiedliche Ergebnisse. Es ist nicht nur im Allgemeinen erheblich langsamer, sondern foreach wird auch erheblich langsamer als der Zugriff per Index. Trotzdem würde ich foreach fast immer einer for-Schleife vorziehen, bei der der Code einfacher wird - denn Lesbarkeit ist fast immer wichtig, Mikrooptimierung dagegen selten.

Jon Skeet
quelle
"Ändern Sie das Array in eine Liste <double> oben, und Sie erhalten radikal unterschiedliche Ergebnisse." Sehr interessant, ich hatte nicht darüber nachgedacht
Johnc
5
Angesichts der merkwürdigen Unterschiede in den Ergebnissen zwischen meinen Tests und den Benchmarks anderer Leute denke ich, dass dies einen Blog-Beitrag verdienen wird ...
Jon Skeet
1
Was bevorzugen Sie fast immer zwischen Arrays und List<T>? Trumpft die Lesbarkeit auch in diesem Fall die Mikrooptimierung?
JohnB
12
@ JohnB: Ja - ich bevorzuge fast immer List<T>Arrays. Die Ausnahmen sind char[]und byte[]werden häufiger als "Datenblöcke" als als normale Sammlungen behandelt.
Jon Skeet
Erstaunlich, ich bekomme einen noch aggressiveren Unterschied auf meiner Maschine, fast 10% zugunsten von foreach auf einfachen Arrays. Ich vermute wild, dass dies alles vom Jitter herrührt, der sich nicht um die zusätzliche Variable, gebundene Prüfungen usw. kümmern muss. Es wäre sehr interessant, wenn jemand eine tiefe Erklärung dafür hat.
Tamir Daniely
162

foreachSchleifen zeigen eine spezifischere Absicht als forSchleifen .

Die Verwendung einer foreachSchleife zeigt jedem, der Ihren Code verwendet, dass Sie vorhaben, jedem Mitglied einer Sammlung etwas anzutun, unabhängig von seinem Platz in der Sammlung. Es zeigt auch, dass Sie die ursprüngliche Sammlung nicht ändern (und löst eine Ausnahme aus, wenn Sie dies versuchen).

Der andere Vorteil von foreachist, dass es auf jedem funktioniert IEnumerable, wo es fornur Sinn macht IList, wo jedes Element tatsächlich einen Index hat.

Wenn Sie jedoch den Index eines Elements verwenden müssen, sollten Sie natürlich eine forSchleife verwenden dürfen. Wenn Sie jedoch keinen Index verwenden müssen, ist es nur unübersichtlich, wenn Sie einen Index haben.

Soweit mir bekannt ist, gibt es keine wesentlichen Auswirkungen auf die Leistung. Irgendwann in der Zukunft ist es möglicherweise einfacher, Code so anzupassen, dass foreacher auf mehreren Kernen ausgeführt wird, aber das ist im Moment kein Grund zur Sorge.

ctford
quelle
23
ctford: Nein, ist es nicht. Der Compiler kann Elemente in sicherlich nicht neu anordnen foreach. foreachhat überhaupt nichts mit funktionaler Programmierung zu tun. Es ist völlig ein Imperativ Paradigma der Programmierung. Sie schreiben Dinge, die in TPL und PLINQ geschehen, falsch zu foreach.
Mehrdad Afshari
13
@BlueTrin: Es garantiert auf jeden Fall die Bestellung (C # -Spezifikation Abschnitt 8.8.4 wird formal foreachals Äquivalent einer whileSchleife definiert). Ich glaube ich weiß, dass sich @ctford bezieht. Die Task Parallel Library ermöglicht es der zugrunde liegenden Sammlung , Elemente in einer beliebigen Reihenfolge bereitzustellen (durch Aufrufen .AsParalleleiner Aufzählung). foreachmacht hier nichts und der Body der Schleife wird auf einem einzelnen Thread ausgeführt . Das einzige, was parallelisiert wird, ist die Erzeugung der Sequenz.
Mehrdad Afshari
6
Enumerable.Select verfügt über eine Überladung, mit der Sie den Index des Elements abrufen können, sodass selbst die Notwendigkeit eines Index keine Verwendung von for erfordert. Siehe msdn.microsoft.com/en-us/library/bb534869.aspx
TrueWill
5
ForEach ist praktisch für die Lesbarkeit und das Speichern der Eingabe. Kosten sind jedoch wichtig; Durch Ändern von 2 oder 3 for-Schleifen in einer von mir erstellten Dokumentdesigner-Benutzeroberfläche von (für jedes Objekt in der Liste) in (für i = 0 in list.count-1) wurde die Antwortzeit von 2-3 Sekunden pro Bearbeitung auf etwa reduziert. 5 Sek. Pro Bearbeitung eines kleinen Dokuments, das nur einige hundert Objekte durchläuft. Selbst für große Dokumente gibt es keine Verlängerung der Zeit, um alle zu durchlaufen. Ich habe keine Ahnung, wie das passiert ist. Was ich weiß ist, dass die Alternative ein kompliziertes Schema war, um nur eine Teilmenge der Objekte zu schleifen. Ich werde jeden Tag die 5-Minuten-Änderung vornehmen! - keine Mikrooptimierung.
FastAl
5
@FastAl Der Unterschied zwischen foreachund die forLeistung für normale Listen beträgt Bruchteile einer Sekunde für die Iteration über Millionen von Elementen, sodass Ihr Problem sicherlich nicht direkt mit jeder Leistung zusammenhängt, zumindest nicht für einige hundert Objekte. Klingt nach einer kaputten Enumerator-Implementierung in der von Ihnen verwendeten Liste.
Mike Marynowski
53

Jedes Mal, wenn es Streitigkeiten über die Leistung gibt, müssen Sie nur einen kleinen Test schreiben, damit Sie quantitative Ergebnisse verwenden können, um Ihren Fall zu unterstützen.

Verwenden Sie die StopWatch-Klasse und wiederholen Sie aus Gründen der Genauigkeit einige Millionen Mal. (Dies könnte ohne eine for-Schleife schwierig sein):

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

Die Daumen drücken, um zu zeigen, dass der Unterschied vernachlässigbar ist, und Sie können genauso gut alles tun, was zu dem am besten zu wartenden Code führt

Rob Fonseca-Ensor
quelle
13
Aber Sie können die Leistung von for und foreach nicht vergleichen. Sie sollen unter verschiedenen Umständen eingesetzt werden.
Michael Krelin - Hacker
7
Ich stimme dir zu Michael, du solltest nicht wählen, welches du basierend auf der Leistung verwenden willst - du solltest das wählen, das am sinnvollsten ist! Aber wenn Ihr Chef sagt "Nicht verwenden, weil es langsamer als foreach ist", dann ist dies die einzige Möglichkeit, ihn davon zu überzeugen, dass der Unterschied vernachlässigbar ist
Rob Fonseca-Ensor
"(Dies kann ohne eine for-Schleife schwierig sein)" Oder Sie können eine while-Schleife verwenden.
Jonescb
49

Es wird immer nah sein. Bei einem Array ist es manchmal for etwas schneller, aber foreachausdrucksstärker und bietet LINQ usw. Im Allgemeinen bleiben Sie bei foreach.

foreachKann in einigen Szenarien zusätzlich optimiert werden. Zum Beispiel kann eine verknüpfte Liste für den Indexer schrecklich sein, aber sie kann schnell vorbei sein foreach. Tatsächlich bietet der Standard LinkedList<T>aus diesem Grund nicht einmal einen Indexer an.

Marc Gravell
quelle
Wollen Sie damit sagen, dass LinkedList<T>es schlanker ist als List<T>? Und wenn ich immer foreach(statt for) verwenden werde, bin ich besser dran LinkedList<T>?
JohnB
2
@ JohnB - nicht schlanker; einfach anders. Beispielsweise verfügt jeder Knoten in einer verknüpften Liste über zusätzliche Referenzen, die für ein flaches Array (das auch zugrunde liegt List<T>) nicht benötigt werden . Es ist mehr, dass das Einsetzen / Entfernen billiger ist .
Marc Gravell
36

Ich vermute, dass es in 99% der Fälle wahrscheinlich nicht signifikant sein wird. Warum sollten Sie also die schnellere anstelle der am besten geeigneten wählen (wie am einfachsten zu verstehen / zu pflegen)?

Brian Rasmussen
quelle
6
@klew, Wenn Sie Ihren Code tatsächlich profilieren, müssten Sie nicht raten, welche 20% so schnell wie möglich sein müssen. Sie würden wahrscheinlich auch herausfinden, dass die tatsächliche Anzahl von Schleifen, die schnell sein müssen, viel geringer ist. Wollen Sie damit wirklich sagen, dass Sie beim Schleifen Ihre Zeit verbringen, im Gegensatz zu dem, was Sie tatsächlich in dieser Schleife tun?
Tster
32

Es ist unwahrscheinlich, dass zwischen beiden ein großer Leistungsunterschied besteht. Wie immer, wenn man mit einem "Was ist schneller?" Frage, sollten Sie immer denken "Ich kann das messen."

Schreiben Sie zwei Schleifen, die dasselbe tun, in den Körper der Schleife, führen Sie beide aus und messen Sie sie zeitlich, und sehen Sie, was der Geschwindigkeitsunterschied ist. Tun Sie dies sowohl mit einem fast leeren Körper als auch mit einem Schleifenkörper, ähnlich dem, was Sie tatsächlich tun werden. Versuchen Sie es auch mit dem von Ihnen verwendeten Sammlungstyp, da verschiedene Arten von Sammlungen unterschiedliche Leistungsmerkmale aufweisen können.

Greg Hewgill
quelle
32

Es gibt sehr gute Gründe, Loops Loops vorzuziehen . Wenn Sie eine Schleife verwenden können, hat Ihr Chef Recht, dass Sie sollten.foreachforforeach

Allerdings durchläuft nicht jede Iteration einfach eine Liste nacheinander. Wenn er es verbietet , ist das falsch.

Wenn ich Sie wäre, würde ich all Ihre natürlichen for-Schleifen in Rekursion verwandeln . Das würde ihn lehren, und es ist auch eine gute mentale Übung für dich.

TED
quelle
Wie ist die Rekursion im Vergleich zu forSchleifen und foreachSchleifen in Bezug auf die Leistung zu vergleichen ?
JohnB
Es hängt davon ab, ob. Wenn Sie die Tail-Rekursion verwenden und Ihr Compiler intelligent genug ist, um dies zu bemerken, kann dies identisch sein. OTOH: Wenn dies nicht der Fall ist und Sie etwas Dummes tun, z. B. viele unnötige (unveränderliche) Daten als Parameter übergeben oder große Strukturen auf dem Stapel als Einheimische deklarieren, kann dies sehr, sehr langsam sein (oder sogar den Arbeitsspeicher verlieren).
Ted
Ähhh. Ich verstehe, warum du das jetzt gefragt hast. Diese Antwort ging auf eine ganz andere Frage. Aus irgendeinem bizarren Grund hat Jonathan Sampson die beiden gestern zusammengelegt. Er hätte das wirklich nicht tun sollen. Die zusammengeführten Antworten machen hier überhaupt keinen Sinn.
TED
18

Jeffrey Richter auf TechEd 2005:

"Ich habe im Laufe der Jahre gelernt, dass der C # -Compiler für mich im Grunde genommen ein Lügner ist." .. "Es liegt an vielen Dingen." .. "Wie wenn Sie eine foreach-Schleife ausführen ..." .. "... das ist eine kleine Codezeile, die Sie schreiben, aber was der C # -Compiler ausspuckt, um dies zu tun, ist phänomenal try / finally-Block dort, innerhalb des finally-Blocks wird Ihre Variable in eine IDisposable-Schnittstelle umgewandelt, und wenn die Umwandlung erfolgreich ist, ruft sie die Dispose-Methode auf, innerhalb der Schleife werden die Current-Eigenschaft und die MoveNext-Methode wiederholt innerhalb der Schleife aufgerufen. Objekte werden unter der Decke erstellt. Viele Leute verwenden foreach, weil es sehr einfach zu codieren ist, sehr einfach zu machen. ".." foreach ist nicht sehr gut in Bezug auf die Leistung.

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

Max Toro
quelle
12

Das ist lächerlich. Es gibt keinen zwingenden Grund, die for-Schleife in Bezug auf die Leistung oder andere zu verbieten.

In Jon Skeets Blog finden Sie einen Leistungsbenchmark und andere Argumente.

Rik
quelle
2
Aktualisierter Link: codeblog.jonskeet.uk/2009/01/29/…
Matt
Das schnellere Schleifenkonstrukt hängt davon ab, was Sie durchlaufen müssen. Ein weiteres Blog, das mehrere Iterationen für mehrere Arten von Objekten wie DataRows und benutzerdefinierte Objekte vergleicht. Es enthält auch die Leistung des While-Schleifenkonstrukts und nicht nur die for- und foreach-Schleifenkonstrukte.
Free Coder 24
11

In Fällen, in denen Sie mit einer Sammlung von Objekten arbeiten, foreachist dies besser. Wenn Sie jedoch eine Zahl erhöhen, ist eine forSchleife besser.

Beachten Sie, dass Sie im letzten Fall Folgendes tun können:

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

Aber es ist sicherlich nicht besser, es hat tatsächlich eine schlechtere Leistung als ein for.

Meta-Knight
quelle
"Besser" ist umstritten: Es ist langsamer und der dnspy-Debugger wird nicht in ein C # foreach eingebrochen (obwohl dies der VS2017-Debugger tun wird). Manchmal besser lesbar, aber wenn Sie Sprachen ohne diese Sprache unterstützen, kann dies ärgerlich sein.
Zeek2
10

Dies sollte Sie retten:

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

Verwenden:

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

Für einen größeren Gewinn können Sie drei Delegierte als Parameter verwenden.

akuhn
quelle
1
Ein winziger Unterschied besteht darin, dass fornormalerweise eine Schleife geschrieben wird, um das Ende des Bereichs auszuschließen (z 0 <= i < 10. B. ). Parallel.Fortut dies auch, um es leicht mit einer gemeinsamen forSchleife austauschbar zu halten .
Groo
9

Sie können darüber in Deep .NET - Teil 1 Iteration lesen

Es deckt die Ergebnisse (ohne die erste Initialisierung) vom .NET-Quellcode bis zur Demontage ab.

Beispiel: Array-Iteration mit einer foreach-Schleife: Geben Sie hier die Bildbeschreibung ein

und - Iteration mit foreach-Schleife auflisten: Geben Sie hier die Bildbeschreibung ein

und das Endergebnis: Geben Sie hier die Bildbeschreibung ein

Geben Sie hier die Bildbeschreibung ein

oder Yaacov
quelle
8

Die Geschwindigkeitsunterschiede in einer for- und einer - foreachSchleife sind winzig, wenn Sie allgemeine Strukturen wie Arrays, Listen usw. durchlaufen und a ausführenLINQ Abfrage über die Sammlung fast immer etwas langsamer durchführen, obwohl das Schreiben besser ist! Wie die anderen Poster bereits sagten, sollten Sie sich eher für Ausdruckskraft als für eine Millisekunde zusätzliche Leistung entscheiden.

Was bisher nicht gesagt wurde, ist, dass eine foreachSchleife beim Kompilieren vom Compiler basierend auf der Sammlung optimiert wird, über die sie iteriert. Das heißt, wenn Sie nicht sicher sind, welche Schleife Sie verwenden sollen, sollten Sie die foreachSchleife verwenden - sie generiert die beste Schleife für Sie, wenn sie kompiliert wird. Es ist auch besser lesbar.

Ein weiterer wichtiger Vorteil der foreachSchleife besteht darin, dass bei Änderungen Ihrer Sammlungsimplementierung ( z. B. von int arrayzu a) List<int>für Ihre foreachSchleife keine Codeänderungen erforderlich sind:

foreach (int i in myCollection)

Das Obige ist unabhängig vom Typ Ihrer Sammlung gleich, während in Ihrer forSchleife Folgendes nicht erstellt wird, wenn Sie myCollectionvon a arrayzu a wechseln List:

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

"Gibt es Argumente, mit denen ich ihn davon überzeugen könnte, dass die for-Schleife akzeptabel ist?"

Nein, wenn Ihr Chef so viel Mikromanagement betreibt, dass er Ihnen sagt, welche Programmiersprachenkonstrukte verwendet werden sollen, können Sie wirklich nichts sagen. Es tut uns leid.

Ken
quelle
7

Dies hängt wahrscheinlich von der Art der Auflistung ab, die Sie auflisten, und von der Implementierung des Indexers. Im Allgemeinen ist die Verwendung foreachwahrscheinlich ein besserer Ansatz.

Es wird auch mit jedem funktionieren IEnumerable- nicht nur mit Indexern.

Andrew Kennan
quelle
7

Dies hat die gleichen zwei Antworten wie die meisten "was schneller ist" -Fragen:

1) Wenn Sie nicht messen, wissen Sie es nicht.

2) (Weil ...) Es kommt darauf an.

Dies hängt davon ab, wie teuer die Methode "MoveNext ()" ist, im Vergleich dazu, wie teuer die Methode "this [int index]" für den Typ (oder die Typen) von IEnumerable ist, über den Sie iterieren werden.

Das Schlüsselwort "foreach" ist eine Abkürzung für eine Reihe von Operationen - es ruft GetEnumerator () einmal in der IEnumerable auf, es ruft MoveNext () einmal pro Iteration auf, es führt eine Typprüfung durch und so weiter. Die Kosten für MoveNext () wirken sich am wahrscheinlichsten auf die Leistungsmessungen aus, da diese O (N) Mal aufgerufen werden. Vielleicht ist es billig, aber vielleicht nicht.

Das Schlüsselwort "for" sieht vorhersehbarer aus, aber in den meisten "for" -Schleifen finden Sie so etwas wie "collection [index]". Dies sieht aus wie eine einfache Array-Indizierungsoperation, ist jedoch ein Methodenaufruf, dessen Kosten vollständig von der Art der Sammlung abhängen, über die Sie iterieren. Wahrscheinlich ist es billig, aber vielleicht nicht.

Wenn die zugrunde liegende Struktur der Sammlung im Wesentlichen eine verknüpfte Liste ist, ist MoveNext spottbillig, aber der Indexer hat möglicherweise O (N) -Kosten, wodurch die tatsächlichen Kosten einer "for" -Schleife O (N * N) entstehen.

NSFW
quelle
6

Jedes Sprachkonstrukt hat eine geeignete Zeit und einen geeigneten Ort für die Verwendung. Es gibt einen Grund, warum die C # -Sprache vier separate Iterationsanweisungen hat - jede ist für einen bestimmten Zweck da und hat eine angemessene Verwendung.

Ich empfehle, sich mit Ihrem Chef zusammenzusetzen und rational zu erklären, warum eine forSchleife einen Zweck hat. Es gibt Zeiten, in denen ein forIterationsblock einen Algorithmus klarer beschreibt als eine foreachIteration. Wenn dies zutrifft, ist es angebracht, sie zu verwenden.

Ich möchte auch Ihren Chef darauf hinweisen - Leistung ist kein Problem und sollte in keiner praktischen Weise ein Problem sein - es geht eher darum, den Algorithmus auf prägnante, aussagekräftige und wartbare Weise auszudrücken. Mikrooptimierungen wie diese verfehlen den Punkt der Leistungsoptimierung vollständig, da jeder echte Leistungsvorteil durch algorithmisches Redesign und Refactoring und nicht durch Loop-Restrukturierung erzielt wird.

Wenn es nach einer rationalen Diskussion immer noch diese autoritäre Sichtweise gibt, liegt es an Ihnen, wie Sie vorgehen sollen. Persönlich würde ich nicht gerne in einem Umfeld arbeiten, in dem rationales Denken entmutigt wird, und würde in Betracht ziehen, bei einem anderen Arbeitgeber in eine andere Position zu wechseln. Ich empfehle jedoch dringend, die Diskussion zu führen, bevor Sie sich aufregen - möglicherweise liegt nur ein einfaches Missverständnis vor.

Reed Copsey
quelle
5

Es ist das, was Sie innerhalb der Schleife tun, das die Leistung beeinflusst, nicht das eigentliche Schleifenkonstrukt (vorausgesetzt, Ihr Fall ist nicht trivial).

Martin Wickman
quelle
5

Ob forist schneller alsforeach wirklich nebensächlich. Ich bezweifle ernsthaft, dass die Auswahl eines über das andere einen erheblichen Einfluss auf Ihre Leistung hat.

Der beste Weg, um Ihre Anwendung zu optimieren, ist die Profilerstellung des tatsächlichen Codes. Dadurch werden die Methoden ermittelt, die die meiste Arbeit / Zeit ausmachen. Optimieren Sie diese zuerst. Wenn die Leistung immer noch nicht akzeptabel ist, wiederholen Sie den Vorgang.

Generell würde ich empfehlen, sich von Mikrooptimierungen fernzuhalten, da diese selten zu signifikanten Gewinnen führen. Die einzige Ausnahme ist die Optimierung identifizierter Hot-Paths (dh wenn Ihre Profilerstellung einige häufig verwendete Methoden identifiziert, kann es sinnvoll sein, diese umfassend zu optimieren).

Brian Rasmussen
quelle
Wenn die einzige Art von Optimierung, die ich in den Projekten, an denen ich arbeite, durchführen müsste, Mikrooptimierungen wären, wäre ich ein glücklicher Camper. Leider ist dies nie der Fall.
Yannick Motton
2
forist geringfügig schneller als foreach. Ich würde dieser Aussage ernsthaft widersprechen. Das hängt ganz von der zugrunde liegenden Sammlung ab. Wenn eine verknüpfte Listenklasse einem Indexer einen ganzzahligen Parameter zur Verfügung stellt, würde ich erwarten, dass eine forSchleife O (n ^ 2) ist, während foreachO (n) erwartet wird.
Mehrdad Afshari
@ Merdad: Eigentlich ist das ein guter Punkt. Ich habe nur über den regulären Fall nachgedacht, eine Liste (dh ein Array) zu indizieren. Ich werde umformulieren, um das zu reflektieren. Vielen Dank.
Brian Rasmussen
@Mehrdad Afshari: Das Indizieren einer Sammlung nach Ganzzahlen kann viel langsamer sein als das Aufzählen. Aber Sie vergleichen tatsächlich die Verwendung von for und eine Indexer-Suche mit der Verwendung foreachvon selbst. Ich denke, die Antwort von @Brian Rasmussen ist richtig, dass sie, abgesehen von der Verwendung mit einer Sammlung, forimmer etwas schneller sein wird als foreach. Außerdem ist foreine Sammlungssuche jedoch immer langsamer als foreachvon selbst.
Daniel Pryden
@ Daniel: Entweder haben Sie ein einfaches Array, für das beide identischen Code generieren, oder es ist ein Indexer beteiligt, wenn Sie eine forAnweisung verwenden. Eine einfache forSchleife mit einer ganzzahligen Steuervariablen ist nicht vergleichbar mit foreach, also ist das raus. Ich verstehe, was @Brian bedeutet und es ist richtig, wie Sie sagen, aber die Antwort kann irreführend sein. Betreff: Ihr letzter Punkt: Nein, tatsächlich ist forOver List<T>immer noch schneller als foreach.
Mehrdad Afshari
4

Die beiden laufen fast genauso. Schreiben Sie einen Code, um beide zu verwenden, und zeigen Sie ihm dann die IL. Es sollte vergleichbare Berechnungen anzeigen, dh keinen Leistungsunterschied.

cjk
quelle
Der Compiler erkennt foreach-Schleifen, die in Arrays / ILists usw. verwendet werden, und ändert sie in for-Schleifen.
Callum Rogers
3
Zeigen Sie ihm unverständliche Beweise dafür, dass es in Ordnung ist, und fordern Sie seinen Beweis an, dass es nicht in Ordnung ist.
cjk
3

for hat eine einfachere Logik zu implementieren, so dass es schneller als foreach ist.

Tarik
quelle
3

Wenn Sie sich nicht in einem bestimmten Prozess zur Geschwindigkeitsoptimierung befinden, würde ich sagen, verwenden Sie die Methode, die den am einfachsten zu lesenden und zu pflegenden Code erzeugt.

Wenn bereits ein Iterator eingerichtet ist, wie bei einer der Auflistungsklassen, ist foreach eine gute und einfache Option. Und wenn es sich um einen ganzzahligen Bereich handelt, den Sie iterieren, ist for wahrscheinlich sauberer.

GeekyMonkey
quelle
3

In den meisten Fällen gibt es wirklich keinen Unterschied.

Normalerweise müssen Sie foreach immer verwenden, wenn Sie keinen expliziten numerischen Index haben, und Sie müssen immer foreach verwenden, wenn Sie keine iterierbare Sammlung haben (z. B. Iteration über ein zweidimensionales Array-Gitter in einem oberen Dreieck). . In einigen Fällen haben Sie die Wahl.

Man könnte argumentieren, dass es etwas schwieriger sein kann, for-Schleifen zu pflegen, wenn magische Zahlen im Code erscheinen. Sie sollten sich zu Recht darüber ärgern, dass Sie keine for-Schleife verwenden können und stattdessen eine Sammlung oder ein Lambda verwenden müssen, um eine Untersammlung zu erstellen, nur weil for-Schleifen verboten wurden.

Cade Roux
quelle
3

Es scheint ein bisschen seltsam, die Verwendung einer for-Schleife völlig zu verbieten.

Es ist ein interessanter Artikel hier , die eine Menge der Leistungsunterschiede zwischen den beiden Schleifen abdeckt.

Ich persönlich würde sagen, dass foreach für Schleifen etwas besser lesbar ist, aber Sie sollten das Beste für den jeweiligen Job verwenden und keinen extra langen Code schreiben müssen, um eine foreach-Schleife einzuschließen, wenn eine for-Schleife besser geeignet ist.

Colethecoder
quelle
Ein wesentliches Zitat aus dem Artikel, auf den Sie verlinken: "... Wenn Sie Hochleistungscode schreiben möchten, der nicht für Sammlungen bestimmt ist, verwenden Sie die for-Schleife. Selbst für Sammlungen kann foreach bei der Verwendung praktisch aussehen, ist aber nicht so effizient."
NickFitz
3

Ich fand die foreachSchleife, die List schneller durchlief . Siehe meine Testergebnisse unten. Im folgenden Code iteriere ich eine arrayGröße der Größen 100, 10000 und 100000 separat mit forund foreachloop, um die Zeit zu messen.

Geben Sie hier die Bildbeschreibung ein

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

AKTUALISIERT

Nach dem @ jgauffin-Vorschlag habe ich den @ johnskeet-Code verwendet und festgestellt, dass die forSchleife mit arrayschneller ist als die folgende.

  • Foreach-Schleife mit Array.
  • For-Schleife mit Liste.
  • Foreach-Schleife mit Liste.

Siehe meine Testergebnisse und Code unten,

Geben Sie hier die Bildbeschreibung ein

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
quelle
3
Dies ist ein sehr schlechter Test. a) Sie führen zu wenige Iterationen durch, um eine endgültige Antwort zu erhalten. b) Dieser Thread. Sleep wird nicht wirklich eine Millisekunde warten. Verwenden Sie die gleiche Methode wie Jon Skeet in seiner Antwort.
Jgauffin
1
99,99% der Zeit wird definitiv im thread.sleep verbracht (was keine Garantie dafür gibt, wie schnell es zurückkehren wird, außer es wird nicht vor mindestens dieser Zeit sein). Das Schleifen ist sehr schnell und das Schlafen ist sehr langsam. Sie verwenden das spätere nicht, um das erstere zu testen.
Ronan Thibaudau
3

Sie können wirklich mit seinem Kopf schrauben und stattdessen einen IQueryable .foreach-Verschluss anstreben:

myList.ForEach(c => Console.WriteLine(c.ToString());
Tad Donaghe
quelle
3
Ich würde Ihre Codezeile durch ersetzen myList.ForEach(Console.WriteLine).
Mehrdad Afshari
2

Ich würde nicht erwarten, dass jemand einen "großen" Leistungsunterschied zwischen den beiden findet.

Ich denke, die Antwort hängt davon ab, ob die Sammlung, auf die Sie zugreifen möchten, eine schnellere Implementierung des Indexerzugriffs oder eine schnellere Implementierung des IEnumerator-Zugriffs aufweist. Da IEnumerator häufig den Indexer verwendet und nur eine Kopie der aktuellen Indexposition enthält, würde ich erwarten, dass der Enumeratorzugriff mindestens so langsam oder langsamer ist als der direkte Indexzugriff, jedoch nicht viel.

Natürlich berücksichtigt diese Antwort keine Optimierungen, die der Compiler möglicherweise implementiert.

JohannesH
quelle
Der C # -Compiler optimiert nur sehr wenig, das überlässt er wirklich dem JITter.
ljs
Nun, der JITter ist ein Compiler ... Richtig?
JohannesH
2

Beachten Sie, dass die for-Schleife und die foreach-Schleife nicht immer gleichwertig sind. Listenaufzähler lösen eine Ausnahme aus, wenn sich die Liste ändert, aber Sie erhalten diese Warnung nicht immer mit einer normalen for-Schleife. Möglicherweise erhalten Sie sogar eine andere Ausnahme, wenn sich die Liste zum falschen Zeitpunkt ändert.

Craig Gidney
quelle
Wenn sich die Liste unter Ihnen ändert, können Sie sich nicht darauf verlassen, dass der Enumerator eine foreach-Schleife unterstützt, um Ihnen dies mitzuteilen. Es wird nicht erneut überprüft, nachdem der Wert an Sie zurückgegeben wurde, was zu einem Rennen führt.
Hoodaticus