Hallo zusammen, Ich programmiere in c# und habe eine Verständnisfrage. Ich habe unten zwei Codevarianten. Was ist bezüglich Geschwindigkeit schneller? Oder sind beide gleich? 1. Varianten foreach(var item in items.where(x => x.abc == typeof(..) && !x.def )){ ...} 2. Variante var filteredItems = items.where(x => x.abc == typeof(..) && !x.def ) foreach(var item in filteredItems ){ ...} Wird die Liste bei jedem Durchlauf neu gefiltert ? Gruß
Schöne Frage. Hier: https://stackoverflow.com/questions/19867012/does-foreach-evaluate-the-array-at-every-iteration Steht in einem ähnlichen Fall dass der enumerator nur einmal erstellt wird. Das where gibt eine Auflistung zurück, die dann von der Schleife zur Abarbeitung benutzt wird. Zur Variante 2: wenn deine items in einer Liste sind, könntest du auch linqs .ForEach auf deine gefilterte elemente verwenden. Das mischt die Syntax nicht so auf. :)
npn schrieb: > Jemand schrieb: >> Stoppuhr kaputt? > > Langeweile? Nein. Das ist die einzige wirklich funktionierende Methode, Geschwindigkeit zu beurteilen.
Jemand schrieb: > Das ist die einzige wirklich funktionierende Methode, > Geschwindigkeit zu beurteilen. Naja, du könntest auch den profiler bemühen, oder application insights. Bei Kleinkram wie hier finde ich den Blick auf die spec aber sinnvoller, weil das die grundsätzliche Frage klärt, und sich nicht nur auf empirische Beobachtung stützt. Davon ab spielen solche Kleinigkeiten imho bei den meisten Applikationen eh keine Rolle, und man sollte die Variante schreiben die les-und wartbarer ist. Bei Performanceproblemen kann man immer noch Profilen.
Beide Varianten sind gleich. Der IL-Code dürfte sich nicht unterscheiden. Die Auswertung geschieht nicht sofort sondern immer bevor die Schleife erneut ausgeführt bzw. beendet wird.
Ausm Bauch raus würd ich sagen kommt auf die Länge der Liste an! bei kurzen Listen scheint mir der unterschied eher marginal falls überhaupt einer besteht, und ich bevorzugte variante 1 bei mittleren Listen kommt mir die zweite variante schneller vor, sofern der Filter nicht millionenfach erneut aufgerufen wird. bei sehr langen Listen allerdings könnte der Filter zusammen mit der angelegten Listenkopie soviel verbrauchen, dass der Vorteil wieder verschwindet. wie gesagt, weder empirisch gemessen, noch in Datenblättern oder Kompilaten nachgesehen, nur so'n Bauchgefühl. (wie wenig auch immer das wert sein mag) 'sid
Wenn ich mir die heutige "Drecks"-Software so ansehe, macht
> Bei Performanceproblemen kann man immer noch Profilen.
das scheinbar keiner.
sid schrieb: > bei sehr langen Listen allerdings könnte der Filter zusammen mit der > angelegten Listenkopie soviel verbrauchen, dass der Vorteil wieder > verschwindet. Es gibt aber keine Kopie der Liste. Mit "Where(...)" erzeugt man keine Listenkopie, sondern eine Aufzählung. Erst während man per Schleife die Aufzählung durchiteriert, wird die Filter-Bedingung ausgewertet, einmal für jedes Element. Und zwar in beiden Fällen. Aber auch das ist im Grunde egal, die beiden Schreibweisen sind äquivalent. In der einen wird ein Zwischenergebnis einer Variablen zugewiesen die im nächsten Statement gleich wieder verwendet wird, in der anderen entfällt lediglich diese Variable, nicht aber das Zwischenergebnis. Ein dem Where folgendes ToList z.B. würde eine Listenkopie erzeugen, und dazu die Bedingung für jedes Element von items auswerten. Die Schleife selbst würde nur noch diese neue Liste durchgehen. Das wäre aber auch in beiden Schreibweisen der Fall. Ich würde empfehlen, sich das Kompilat mit ilSpy, dnSpy, dotPeek oder wie sie alle heißen anzuschauen. Spoiler: Dann sähe man, dass für foreach zunächst einmal der Ausdruck nach dem "in" ausgewertet wird, dann vom Ergebnis die Methode GetEnumerator() aufgerufen wird. Mit dem enumerator wird dann die Schleife "bewältigt" mit wiederholten aufrufen von MoveNext() und Current. Im Aufruf von MoveNext() geschieht die eigentliche Auswertung der Filterbedingungen, bis EIN weiteres passendes Element oder das Ende gefunden wird.
sparfux schrieb: > Wenn ich mir die heutige "Drecks"-Software so ansehe, macht > >> Bei Performanceproblemen kann man immer noch Profilen. > > das scheinbar keiner. Macht, ja auch wenig Spaß, Marketing will Features sehen und außerdem gibt es immer einen im Team der in den frühen 90 mal ein Buch gelesen hat und daher fest überzeugt ist dass er das bottleneck durch pure Erfahrung und Codelesen findet und mit einem kruden Konstrukt vermeiden kann.
Sascha R. schrieb: > Es gibt aber keine Kopie der Liste. Mit "Where(...)" erzeugt man keine > Listenkopie, sondern eine Aufzählung. ich sollte vor'm schlafengehen keine Codezeilen mehr lesen.. ich hätte schwören können da .ToList() gelesen gehabt zu haben (uuups) Du hast aber insofern Recht, alsdass ein Enumerator hier deutlich kleinere items trägt als die ursprüngliche Liste.. dennoch bleibt er dem Grunde nach eine Liste* und kostet Speicher, und Zeit zum erstellen ;) Macht aber meinen letzten Gedanken in der Tat obsolet.. kann mir nicht vorstellen, dass man mit IEnumerable<T> ausreichend Speicher verbrauchen kann um in ein Performanceproblem zu rennen. 'sid * sollte in etwa vergleichbar viel Platz benötigen wie List<int> meine ich #kopfkratz
sparfux schrieb: > Wenn ich mir die heutige "Drecks"-Software so ansehe Ich gehe mal davon aus du programmierst noch direkt in Maschinencode, und benutzt maximal edlin oder vi auf einem 24x80 Terminalfenster. Denn die modernen Entwicklungsumgebungen sind ja alles "Dreckssoftware" :-) SCNR
sid schrieb: > Du hast aber insofern Recht, alsdass ein Enumerator hier deutlich > kleinere items trägt als die ursprüngliche Liste.. > dennoch bleibt er dem Grunde nach eine Liste* und kostet Speicher, und > Zeit zum erstellen ;) Ich kenne mich in C# nicht aus, vermute aber, dass ein Enumerator in C# prinzipiell dasselbe ist wie ein Iterator in Python und damit unabhängig von der Länge der Eingabesequenz (die sogar endlos sein kann) nur konstanten Speicherplatz belegt. Die eigentliche Filterung wird nicht beum Aufruf von where, sondern erst bei der Abarbeitung des Enumerators bzw. Iterators (bspw. in einer foreach-Schleife) durchgeführt. Auch die Filter-Funktion in Haskell arbeitet nach diesem Prinzip, nur ist das dort dort nichts Besonderes, da in Haskell auch alle anderen Funktionen defaultmäßig lazy, d.h erst bei Bedarf ausgewertet werden.
Yalu X. schrieb: > vermute aber, dass ein Enumerator in C# > prinzipiell dasselbe ist wie ein Iterator in Python und damit unabhängig > von der Länge der Eingabesequenz (die sogar endlos sein kann) nur > konstanten Speicherplatz belegt. Die eigentliche Filterung wird nicht > beum Aufruf von where, sondern erst bei der Abarbeitung des Enumerators > bzw. Iterators (bspw. in einer foreach-Schleife) durchgeführt. Ja so ist das im Prinzip. Wobei hier noch unterschieden wird zwischen IEnumerable (kann Enumerator(en) bereitstellen, hat selbst keine Position) und IEnumerator (kann einmal durch die Aufzählung führen, beinhaltet implizit eine aktuelle Position). Where liefert IEnumerable, foreach holt sich von diesem dann den Enumerator. Allerdings geschieht in beiden Varianten der Ausgangsfrage genau das. Einmal in getrennten Statements (Zuweisung + foreach), einmal in einem kombinierten Statement (foreach).
:
Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.