jimpanzer jimpanzer - 3 months ago 12
C# Question

Every iteration of every “foreach” loop generated 24 Bytes of garbage memory?

My friend works with Unity3D on C#. And he told me specifically:


Every iteration of every “foreach” loop generated 24 Bytes of garbage
memory.


And I also see this information here

But is this true?

The paragraph most relevant to my question:


Replace the “foreach” loops with simple “for” loops. For some reason,
every iteration of every “foreach” loop generated 24 Bytes of garbage
memory. A simple loop iterating 10 times left 240 Bytes of memory
ready to be collected which was just unacceptable

Answer

foreach is an interesting beast; a lot of people mistakenly think it is tied to IEnumerable[<T>] / IEnumerator[<T>], but that simply isn't the case. As such, it is meaningless to say:

Every iteration of every “foreach” loop generated 24 Bytes of garbage memory.

In particular, it depends on exactly what you are looping over. For example, a lot of types have custom iterators - List<T>, for example - has a struct-based iterator. The following allocates zero objects:

// assume someList is a List<SomeType>
foreach(var item in someList) {
   // do something with item
}

However, this one does object allocations:

IList<SomeType> data = someList;
foreach(var item in data) {
   // do something with item
}

Which looks identical, but isn't - by changing the foreach to iterate over IList<SomeType> (which doesn't have a custom GetEnumerator() method, but which implements IEnumerable<SomeType>) we are forcing it to use the IEnumerable[<T>] / IEnumerator<T> API, which much by necessity involve an object.

Edit caveat: note I am assuming here that unity can preserve the value-type semantics of the custom iterator; if unity is forced to elevate structs to objects, then frankly all bets are off.

As it happens, I'd be happy to say "sure, use for and an indexer in that case" if every allocation matters, but fundamentally: a blanket statement on foreach is unhelpful and misleading.

Comments