2009-08-22 3 views
6

Wie wirkt sich die Verwendung von vielen Iteratoren in C# auf die Speichernutzung aus? Nehmen wir ein Programm an, das Tausende von foreach-Schleifen ausführt - weist jede Schleife ein temporäres Objekt auf dem Heap über einen Aufruf an GetEnumerator zu? Führt die CLR irgendeine Art von Optimierung durch (z. B. Stapelzuordnung von IEnumerator Objekten)? Oder ist das einfach nicht wichtig genug, um sich Sorgen zu machen?Speichernutzung von Iteratoren in C#

Antwort

13

Es ist einfach nicht signifikant genug, um sich in den meisten Fällen Sorgen zu machen. Wie Eric hervorhebt, gibt es möglicherweise einige Fälle, in denen es wichtig ist, aber sie sind ziemlich wenige und weit in meiner Erfahrung.

Wenn Sie Hunderttausende von foreach Schleifen tun, vermutlich tun Sie tatsächlich Arbeit innerhalb dieser Schleifen. Das wird mit ziemlicher Sicherheit wichtiger sein als die Iteratoren selbst.

Beachten Sie, dass die Verwendung von foreach über ein Array (das zur Kompilierungszeit als Array bekannt ist) sowieso nicht IEnumerable<T> verwendet - es verwendet direkte Indizierung. Ich würde meinen Code auf dieser Grundlage jedoch nicht ändern.

Wie immer, wenn Sie über die Leistung besorgt sind, müssen Sie es messen und profilieren. Engpässe sind fast nie dort, wo Sie sie erwarten.

+13

Obwohl dies, wie immer, ein exzellenter Rat Jon war, zeigten unsere Leistungstests in einigen realen Szenarien einen signifikanten Leistungseinfluss durch die Heapzuweisung/Speicherbereinigung von Iteratoren. Deshalb ist der Enumerator der Liste ein böser veränderbarer Werttyp und kein Referenztyp. Aber natürlich ist das Stichwort "unser Leistungstest" - wie Sie bemerken, ist es töricht, Leistungsentscheidungen ohne Daten zu treffen. –

+2

Ah ja, die bösen Mutable 'List ' Iteratoren - Ich erinnere mich an einen Newsgroup-Post mit etwas sah wie ein vernünftiges Stück Code, aber die einige sehr seltsame Ergebnisse aufgrund genau dieser Design-Entscheidung :) Ich werde die Antwort bearbeiten Wie dem auch sei ... Bloße Behauptungen von "es ist nicht wichtig" sind selten eine gute Idee. –

2

Oft kann der Compiler eine foreach-Schleife in eine einfache Schleife optimieren, die nur eine Indexvariable auf dem Stack (oder in einem Prozessorregister) benötigt.

Wenn immer noch ein Iterator verwendet wird, sind die meisten davon Strukturen, so dass sie nur auf dem Stapel statt auf dem Heap zugewiesen werden.

Die wenigen Iteratoren, die Klassen sind, sind immer noch recht klein und schnell. Sie könnten Millionen von ihnen ohne spürbare Auswirkungen erstellen.

+0

Klingt gut ... Kurze Folgefrage: Wenn ich die IEnumerable-Schnittstelle implementiere, gibt die GetEnumerator-Methode definitionsgemäß einen IEnumerator zurück. Wenn mein benutzerdefinierter Iterator eine Struktur ist, gehe ich davon aus, dass er daher als ein Box-Objekt zurückgegeben wird? –

+6

Jen, _carefully_ sehen, wie List tut es, und Sie werden sehen, wie Sie das Boxen zu vermeiden. Der Trick ist, dass * die "foreach" -Schleife nicht wirklich erfordert, dass GetEnumerator IEnumerator * zurückgibt. Solange es etwas zurückgibt, das die richtigen Eigenschaften und Methoden hat, bist du golden. Sie können dadurch das Boxen vermeiden, indem Sie einen öffentlichen GetEnumerator, der eine Struktur zurückgibt, und einen expliziten IEnumerable .GetEnumerator, der eine Boxed-Struktur zurückgibt. –

+4

Aber noch einmal, wie Jon sagt, tun Sie nicht diese doof Sache, wenn Sie solide Daten haben, dass das Erstellen von Enumeratoren auf dem Heap eine Hauptursache für ein wirkliches, kundenbeeinträchtigendes Leistungsproblem ist. Veränderbare Werttypen verursachen alle Arten von seltsamen Problemen, die teuer zu debuggen und schwer zu verstehen sind. –