Each iteration of each foreach loop generated 24 bytes of garbage memory?

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

Each iteration of each foreach loop generates 24 bytes of garbage memory.

And I also see this information here

But is it?

The most important point for my question:

Replace foreach loops with simple "for" loops. For some reason, each iteration of each foreach loop generates 24 bytes of memory garbage. A simple loop repeating 10 times left 240 bytes of memory ready for collection, which was simply unacceptable

+7
garbage-collection c # foreach mono unity3d
source share
3 answers

foreach - an interesting beast; many people mistakenly believe that it is tied to IEnumerable[<T>] / IEnumerator[<T>] , but this is simply not so. Therefore, it makes no sense to say:

Each iteration of each foreach loop generates 24 bytes of garbage memory.

In particular, it depends on what you are looping. For example, many types have custom iterators - List<T> , for example, has a struct based iterator. Null objects are highlighted below:

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

However, this object selects objects:

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

Which looks identical, but not - by changing the foreach to iterate over IList<SomeType> (which does not have a custom GetEnumerator() method, but which implements IEnumerable<SomeType> ), we force it to use the IEnumerable[<T>] / IEnumerator<T> API IEnumerator<T> , which is necessarily associated with the object.

Edit caveat: note I assume here that unity can preserve the semantics of the value of a custom iterator type; if unity is forced to erect structures to objects, then, frankly, all bets are off.

Be that as it may, I would be glad to say “be sure to use for and the index in this case” if each distribution matters, but in principle: the expression on the clothes on foreach useless and misleading.

+11
source share

Your friend is right, the following code will generate 24k garbage every frame in Unity from version 4.3.4:

 using UnityEngine; using System.Collections.Generic; public class ForeachTest : MonoBehaviour { private string _testString = "this is a test"; private List<string> _testList; private const int _numIterations = 10000; void Start() { _testList = new List<string>(); for(int i = 0; i < _numIterations; ++i) { _testList.Add(_testString); } } void Update() { ForeachIter(); } private void ForeachIter() { string s; for(int i = 0; i < 1000; ++i) { foreach(string str in _testList) { s = str; } } } } 

This shows the distribution in the Unity profiler:

Unity Profiler showing 24k of allocation per update

According to the following link, this is a bug in the mono version used by Unity, which forces the structure enumerator to get a box, which seems like a reasonable explanation, although I did not check the code check: Blog post about boxing error

+10
source share

There are some good tips about foreach here, but I have to comment on the Mark comment above regarding tags (unfortunately I have not enough comments to comment on his comment), where he stated

Actually, I have to take this article with salt, because it claims "Calling a tag property on an object that allocates and copies additional memory", where the tag here is a string - sorry, but this is simply not true - all that happens is that that a link to an existing string object is copied onto the stack - there is no superfluous placement of the object here. -

Actually, calling the tag property creates garbage. I assume that the inner tag is stored as integers (or some other simple type), and when the tag property is called, Unity converts the int to a string using the inner table.

It ignores the reason, since the tag property can just as easily return a row from this internal table, rather than create a new row, but for some reason it is.

You can verify this yourself using the following simple component:

 public class TagGarbageTest : MonoBehaviour { public int iterations = 1000; string s; void Start() { for (int i = 0; i < iterations; i++) s = gameObject.tag; } } 

If gameObject.tag did not produce garbage, then increasing / decreasing the number of iterations would not affect the distribution of GC (in Unity Profiler). In fact, we see a linear increase in the GC distribution with an increase in the number of iterations.

It's also worth noting that the name property behaves in exactly the same way (again, the reason why it eludes me).

+5
source share

All Articles