Welches Code-Snippet bietet eine bessere Leistung? Die folgenden Codesegmente wurden in C # geschrieben.
1.
for(int counter=0; counter<list.Count; counter++)
{
list[counter].DoSomething();
}
2.
foreach(MyType current in list)
{
current.DoSomething();
}
c#
performance
for-loop
foreach
Kthevar
quelle
quelle
list
tatsächlich eincount
Mitglied anstelle vonCount
.Antworten:
Nun, es hängt teilweise von der genauen Art ab
list
. Dies hängt auch von der genauen CLR ab, die Sie verwenden.Ob es in irgendeiner Weise von Bedeutung ist oder nicht, hängt davon ab, ob Sie echte Arbeit in der Schleife leisten. In fast allen Fällen ist der Unterschied zur Leistung nicht signifikant, aber der Unterschied zur Lesbarkeit begünstigt die
foreach
Schleife.Ich persönlich würde LINQ verwenden, um das "Wenn" zu vermeiden:
BEARBEITEN: Für diejenigen unter Ihnen, die behaupten, dass das Iterieren über a
List<T>
mitforeach
denselben Code wie diefor
Schleife erzeugt, gibt es hier Beweise dafür, dass dies nicht der Fall ist:Produziert IL von:
Der Compiler behandelt Arrays unterschiedlich und konvertiert eine
foreach
Schleife grundsätzlich in einefor
Schleife, jedoch nichtList<T>
. Hier ist der entsprechende Code für ein Array:Interessanterweise kann ich dies nirgendwo in der C # 3-Spezifikation finden ...
quelle
List<T>
.foreach
über ein Arrayfor
sowieso gleichbedeutend ist. Immer Code zur besseren Lesbarkeit zuerst, dann nur Mikro-optimize , wenn Sie haben Beweise , dass es einen messbaren Leistungsvorteil ergibt.Eine
for
Schleife wird zu einem Code kompiliert, der ungefähr dem entspricht:Wobei als
foreach
Schleife ein Code kompiliert wird, der ungefähr dem entspricht:Wie Sie sehen, hängt alles davon ab, wie der Enumerator implementiert ist und wie der Listenindexer implementiert ist. Wie sich herausstellt, wird der Enumerator für auf Arrays basierende Typen normalerweise wie folgt geschrieben:
Wie Sie sehen, macht es in diesem Fall keinen großen Unterschied, aber der Enumerator für eine verknüpfte Liste würde wahrscheinlich ungefähr so aussehen:
In .NET werden Sie feststellen, dass die LinkedList <T> -Klasse nicht einmal über einen Indexer verfügt, sodass Sie Ihre for-Schleife nicht für eine verknüpfte Liste ausführen können. aber wenn Sie könnten, müsste der Indexer so geschrieben werden:
Wie Sie sehen können, ist das mehrmalige Aufrufen in einer Schleife viel langsamer als die Verwendung eines Enumerators, der sich daran erinnern kann, wo er sich in der Liste befindet.
quelle
Ein einfacher Test zur Halbvalidierung. Ich habe einen kleinen Test gemacht, nur um zu sehen. Hier ist der Code:
Und hier ist der foreach-Abschnitt:
Als ich das for durch ein foreach ersetzte - das foreach war 20 Millisekunden schneller - konstant . Das For war 135-139 ms, während das Foreach 113-119 ms betrug. Ich habe mehrmals hin und her gewechselt, um sicherzugehen, dass es kein Prozess war, der gerade erst begonnen hat.
Als ich jedoch das foo und die if-Anweisung entfernte, war das for um 30 ms schneller (foreach war 88 ms und for war 59 ms). Sie waren beide leere Muscheln. Ich gehe davon aus, dass foreach tatsächlich eine Variable übergeben hat, bei der for nur eine Variable inkrementiert hat. Wenn ich hinzufügte
Dann wird das for um ca. 30ms langsam. Ich gehe davon aus, dass dies damit zu tun hat, dass foo erstellt, die Variable im Array erfasst und foo zugewiesen wird. Wenn Sie nur auf intList [i] zugreifen, haben Sie diese Strafe nicht.
Um ehrlich zu sein. Ich habe erwartet, dass der Foreach unter allen Umständen etwas langsamer ist, aber nicht genug, um in den meisten Anwendungen eine Rolle zu spielen.
Bearbeiten: Hier ist der neue Code mit Jons Vorschlägen (134217728 ist der größte Int, den Sie haben können, bevor die System.OutOfMemory-Ausnahme ausgelöst wird):
Und hier sind die Ergebnisse:
Daten generieren. Berechnung für Schleife: 2458 ms Berechnung für jede Schleife: 2005 ms
Wenn Sie sie austauschen, um zu sehen, ob es sich um die Reihenfolge der Dinge handelt, erhalten Sie (fast) die gleichen Ergebnisse.
quelle
Hinweis: Diese Antwort gilt mehr für Java als für C #, da C # keinen Indexer aktiviert hat
LinkedLists
, aber ich denke, der allgemeine Punkt gilt immer noch.Wenn es sich bei dem
list
, mit dem Sie arbeiten, um a handeltLinkedList
, ist die Leistung des Indexer-Codes ( Zugriff im Array-Stil ) viel schlechter als bei Verwendung desIEnumerator
from -Codesforeach
für große Listen.Wenn Sie
LinkedList
mit der Indexersyntax: auf ein Element 10.000 zugreifenlist[10000]
, beginnt die verknüpfte Liste amNext
Kopfknoten und durchläuft den Zeiger zehntausend Mal, bis das richtige Objekt erreicht ist. Wenn Sie dies in einer Schleife tun, erhalten Sie natürlich:Wenn Sie aufrufen
GetEnumerator
(implizit mit derforach
-syntax), erhalten Sie einIEnumerator
Objekt, das einen Zeiger auf den Kopfknoten hat. Bei jedem AufrufMoveNext
wird dieser Zeiger wie folgt zum nächsten Knoten verschoben:Wie Sie sehen können, wird im Fall von
LinkedList
s die Array-Indexer-Methode immer langsamer, je länger Sie eine Schleife ausführen (sie muss immer wieder denselben Kopfzeiger durchlaufen). Während derIEnumerable
Gerechte in konstanter Zeit arbeitet.Natürlich, wie Jon sagte, hängt dies wirklich von der Art ab
list
, wenn daslist
nicht einLinkedList
, sondern ein Array ist, ist das Verhalten völlig anders.quelle
LinkedList<T>
Dokumente auf MSDN an und es hat eine ziemlich anständige API. Am wichtigsten ist, dass es keineget(int index)
Methode gibt, wie es Java tut. Dennoch denke ich, dass der Punkt immer noch für jede andere listenartige Datenstruktur gilt, die einen Indexer verfügbar macht, der langsamer als ein bestimmter istIEnumerator
.Wie andere Leute bereits erwähnt haben, obwohl die Leistung eigentlich nicht viel ausmacht, wird der foreach aufgrund der
IEnumerable
/IEnumerator
Verwendung in der Schleife immer etwas langsamer sein . Der Compiler übersetzt das Konstrukt in Aufrufe an dieser Schnittstelle und für jeden Schritt werden eine Funktion + eine Eigenschaft im foreach-Konstrukt aufgerufen.Dies ist die äquivalente Erweiterung des Konstrukts in C #. Sie können sich vorstellen, wie sich die Auswirkungen auf die Leistung je nach den Implementierungen von MoveNext und Current unterscheiden können. Während bei einem Array-Zugriff diese Abhängigkeiten nicht bestehen.
quelle
List<T>
hier ist, gibt es immer noch den Treffer (möglicherweise inline), den Indexer aufzurufen. Es ist nicht so, als wäre es ein Bare-Metal-Array-Zugang.Nachdem ich genug Argumente gelesen habe, dass "die foreach-Schleife aus Gründen der Lesbarkeit bevorzugt werden sollte", kann ich sagen, dass meine erste Reaktion "was" war? Die Lesbarkeit ist im Allgemeinen subjektiv und in diesem speziellen Fall sogar noch größer. Für jemanden mit Programmierhintergrund (praktisch jede Sprache vor Java) sind for-Schleifen viel einfacher zu lesen als foreach-Schleifen. Darüber hinaus unterstützen dieselben Personen, die behaupten, dass foreach-Schleifen besser lesbar sind, auch linq und andere "Funktionen", die das Lesen und Verwalten von Code erschweren, was den obigen Punkt beweist.
Informationen zu den Auswirkungen auf die Leistung finden Sie in der Antwort auf diese Frage.
BEARBEITEN: Es gibt Sammlungen in C # (wie das HashSet), die keinen Indexer haben. In diesen Sammlungen foreach ist der einzige Weg zu iterieren , und es ist der einzige Fall , ich denke , es sollte verwendet werden , über für .
quelle
Es gibt eine weitere interessante Tatsache, die beim Testen der Geschwindigkeit beider Schleifen leicht übersehen werden kann: Durch die Verwendung des Debug-Modus kann der Compiler den Code nicht mit den Standardeinstellungen optimieren.
Dies führte mich zu dem interessanten Ergebnis, dass foreach schneller ist als im Debug-Modus. Während das for im Release-Modus schneller ist als foreach. Offensichtlich hat der Compiler bessere Möglichkeiten, eine for-Schleife zu optimieren als eine foreach-Schleife, die mehrere Methodenaufrufe gefährdet. Eine for-Schleife ist übrigens so grundlegend, dass es möglich ist, dass dies sogar von der CPU selbst optimiert wird.
quelle
In dem von Ihnen angegebenen Beispiel ist es definitiv besser, eine
foreach
Schleife anstelle einerfor
Schleife zu verwenden.Das Standardkonstrukt
foreach
kann schneller sein (1,5 Zyklen pro Schritt) als ein einfachesfor-loop
(2 Zyklen pro Schritt), es sei denn, die Schleife wurde abgewickelt (1,0 Zyklen pro Schritt).Also für den täglichen Code ist, Leistung kein Grund , die komplexeren zu verwenden
for
,while
oderdo-while
Konstrukte.Schauen Sie sich diesen Link an: http://www.codeproject.com/Articles/146797/Fast-and-Less-Fast-Loops-in-C
quelle
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:
und - Iteration mit foreach-Schleife auflisten:
und das Endergebnis:
quelle