Mein Freund arbeitet mit Unity3D auf C #. Und er sagte mir ausdrücklich:
Jede Iteration jeder "foreach" -Schleife erzeugte 24 Bytes Müll Speicher.
Und ich sehe auch diese Information hier
Aber ist das wahr?
Der für meine Frage relevanteste Absatz:
Ersetzen Sie die "foreach" -Schleifen durch einfache "for" -Schleifen. Aus irgendeinem Grund, Jede Iteration jeder "foreach" -Schleife erzeugte 24 Bytes Müll Erinnerung. Eine einfache Schleife, die 10 Mal iterierte, ließ 240 Bytes Speicher übrig bereit, gesammelt zu werden, was einfach inakzeptabel war
foreach
ist ein interessantes Biest; Viele Leute glauben fälschlicherweise, dass es an IEnumerable[<T>]
/ IEnumerator[<T>]
gebunden ist, aber das ist einfach nicht der Fall. Daher ist es sinnlos zu sagen:
Jede Iteration jeder "foreach" -Schleife erzeugte 24 Bytes Müllspeicher.
Insbesondere hängt es genau davon ab, was Sie durchlaufen. Zum Beispiel haben viele Typen benutzerdefinierte Iteratoren - List<T>
zum Beispiel - hat einen struct
-basierten Iterator. Folgendes weist null Objekte zu:
Allerdings tut dieser Objektzuweisungen:
%Vor% Was identisch aussieht, aber nicht - indem wir die foreach
auf IList<SomeType>
iterieren (die keine benutzerdefinierte GetEnumerator()
-Methode hat, die aber IEnumerable<SomeType>
implementiert), zwingen wir sie zu verwenden die IEnumerable[<T>]
/ IEnumerator<T>
API, die notwendigerweise ein Objekt involviert.
Bearbeiten caveat: Anmerkung Ich gehe hier davon aus, dass die Einheit die Werttyp-Semantik des benutzerdefinierten Iterators beibehalten kann. Wenn die Einheit gezwungen ist, Strukturen zu Objekten zu erheben, dann sind alle Wetten offen.
Wie auch immer, ich würde gerne sagen "sicher, verwenden for
und ein Indexer in diesem Fall", wenn jede Zuteilung von Bedeutung ist, aber grundsätzlich: eine pauschale Aussage über foreach
ist nicht hilfreich und irreführend.
Ihr Freund ist korrekt, der folgende Code wird 24k Müll pro Frame in Unity ab Version 4.3.4 generieren:
%Vor%Dies zeigt die Zuweisung im Unity-Profiler:
Laut dem folgenden Link ist es ein Fehler in der Version von mono, die von Unity verwendet wird, dass der Struct-Enumerator geboxt wird. Das scheint eine vernünftige Erklärung zu sein, obwohl ich nicht durch Code-Inspektion verifiziert habe: Blog-Post diskutiert den Box-Bug
Es gibt hier einige gute Ratschläge bezüglich foreach, aber ich muss über den Kommentar des Marks, der sich auf Tags bezieht (ich habe leider nicht genügend Rep, um unter seinem Kommentar zu kommentieren) zu plappern, wo er sagte:
>Eigentlich muss ich diesen Artikel mit einer Prise Salz nehmen, weil es so ist Ansprüche "Aufruf der Tag-Eigenschaft für ein Objekt zuweist und kopiert zusätzlicher Speicher "- wo Tag hier ist eine Zeichenfolge - sorry, aber das einfach ist nicht wahr - alles was passiert ist, dass der Verweis auf die Das vorhandene String-Objekt wird auf den Stack kopiert - es gibt kein Extra Objektverteilung hier. -
Tatsächlich ruft das Aufrufen der Tag-Eigenschaft Müll hervor. Meine Vermutung ist, dass intern die Tags als Ganzzahlen (oder ein anderer einfacher Typ) gespeichert werden, und wenn die Tag-Eigenschaft aufgerufen wird, konvertiert Unity das int in eine Zeichenfolge unter Verwendung einer internen Tabelle.
Es widerspricht der Vernunft, da die tag -Eigenschaft genauso gut die Zeichenfolge aus dieser internen Tabelle zurückgeben kann, anstatt eine neue Zeichenfolge zu erstellen, aber aus welchem Grund auch immer, so funktioniert es.
Sie können dies selbst mit der folgenden einfachen Komponente testen:
%Vor%Wenn gameObject.tag keinen Müll produzierte, hätte das Erhöhen / Verringern der Anzahl der Iterationen keine Auswirkung auf die GC-Zuweisung (im Unity Profiler). Tatsächlich sehen wir einen linearen Anstieg der GC-Allocation, wenn die Anzahl der Iterationen zunimmt.
Bemerkenswert ist auch, dass sich die Namenseigenschaft auf genau die gleiche Weise verhält (wiederum der Grund, warum sich mir entzieht).
Tags und Links c# garbage-collection mono unity3d foreach