Mastering C# and Unity3D

Do Foreach Loops Still Create Garbage?

Over a year ago I wrote an article title Do Foreach Loops Create Garbage using Unity 5.2 and tested foreach with a variety of collections: List, Dictionary, arrays, etc. Since then Unity has a new C# compiler and version 5.6 has been released. Is it safe to use foreach now? Read on to find out!

Today’s article is simply a re-test of the same exact test script from the previous article. Instead of Unity 5.2.2f1, I’ve tested today with 5.6.0f3. All of the code and steps to run it still apply.

As a reminder, the test script uses a foreach loop on a variety of collection types (e.g. List) to see if any garbage is created. It then runs another foreach loop to see if any garbage is created the second time, which may not be the case if there’s some internal caching going on.

Now on to the huge table of results. I’ve added a “5.2 Total” column on the right side to show the previous results.

Type

GetEnumerator()

MoveNext()

Current

Dispose()

5.6 Total

5.2 Total

1st

2nd

1st

2nd

1st

2nd

1st

2nd

1st

2nd

1st

2nd

Array

n/a

n/a

n/a

n/a

n/a

n/a

n/a

n/a

0

0

0

0

ArrayList

40

40

0

0

0

0

n/a

n/a

40

40

40

40

BitArray

40

40

0

0

17

17

n/a

n/a

57

57

57

57

Dictionary<T>

32

0

32

0

0

0

0

0

64

0

72

40

HashSet<T>

32

0

0

0

0

0

0

0

32

0

40

40

Hashtable

56

56

0

0

32

32

n/a

n/a

88

88

88

88

IEnumerable

40

40

0

0

20

20

0

0

92

60

92

60

IEnumerable<T>

40

40

0

0

0

0

0

0

40

40

40

40

LinkedList<T>

32

0

0

0

0

0

0

0

32

0

48

48

List<T>

0

0

0

0

0

0

0

0

0

0

40

40

Queue

32

32

0

0

0

0

n/a

n/a

32

32

32

32

Queue<T>

32

0

0

0

0

0

0

0

32

0

32

32

SortedDictionary<TKey, TValue>

32

0

256

192

0

0

0

0

288

192

304

240

SortedList

178

64

0

0

32

32

n/a

n/a

210

96

210

96

Stack

32

32

0

0

0

0

n/a

n/a

32

32

32

32

Stack<T>

32

0

0

0

0

0

0

0

32

0

32

32

Dictionary<T>, HashSet<T>, LinkedList<T>, Queue<T>, and Stack<T> now create the same, 8, or 16 bytes less garbage on the first loop and zero garbage on any loops thereafter. That’s great news because there’s no way to use a plain for loop instead of foreach with any of these collections.

SortedDictionary<TKey, TValue> now creates 16 bytes less garbage on the first loop and 64 bytes on subsequent loops. It’s still the biggest garbage creator of any collection, so try to avoid it if you’re keeping clear of the GC.

Finally the big news: List<T> no longer creates any garbage! This is by far the most common collection, so this is a huge win for us Unity programmers! Of course there is a caveat to that. In order for the foreach loop to not create any garbage, you need to use a List<T> variable. You can’t use an interface that it implements like IEnumerable<T> or 40 bytes of garbage will be created every loop. Normally you can avoid that though, and you should if you want to take advantage of a garbage-free foreach.

While all the other collection types haven’t changed the amount of garbage they create, at least none of them use more garbage than they did back in Unity 5.2. So we have nothing but good news today. If you’re using foreach because you can’t use for then you’re not going to be creating as much garbage as before with common collections like Dictionary<T>. With List<T> you won’t be creating any garbage, so there’s no need to use for with them anymore.

I did a test of for versus foreach last May using Unity 5.3 and found that foreach was quite a lot slower than for. That may have changed with the new compiler and other advancements in Unity 5.4-5.6, so I’ll make a note to re-test.

Add two of your TestScript component, and you’ll see that the ‘FirstTest’ has the same cost as the ‘SecondTest’ on the second component. The difference is most likely just static initialization, i.e. you don’t pay the cost per-object. For all practical consideration, most generic containers now have 0 memory leak on foreach.

You’re right that I didn’t explicitly state in the article that the subsequent foreach loops represented by the 2nd column apply to all instances of that collection type, not just the single instance. It is static initialization, not instance initialization.

You’re also close to right about generic containers not creating any garbage due to foreach. It’s still true that SortedDictionary creates (a lot) of garbage and any container typed as IEnumerable will create garbage. SortedDictionary isn’t very common, so that’s probably not a concern, but it’s still important to use the exact type—not an implemented interface—of the container in order to avoid the IEnumerable cost.