I am reading the book “Writing High-Performance Dot Net Code” and it makes the following assertion:
There is one fundamental rule for high-performance programming with regard to the garbage collectors. In fact the garbage collector was explicitly designed with this idea in mind:
Collect objects in gen0 or not at all.
Put differently, you want objects to have an extremely short lifetime so that garbage collector will never touch them at all, or, if you cannot do that, they should go to gen 2 as fast as possible and stay there forever, never to be collected.
My own profiling on Windows .NET apps definitely matches this. Gen0 collection was blisteringly fast compared to gen1 and gen2.
I’ve done a bit of profiling in Unity and it seems to hold up. 10 million iterations of a SortedDictionary is producing tons of garbage (132 KB per frame), but GC.Collect is only taking 0.4 milliseconds on desktop. I’m wary about drawing conclusions based on how little I know about Unity’s collector.
Does anyone else know more? I don’t see any reference to GC generations in Unity from just googling.
Stop right there, what you’re reading will be of no help to you in regards to the mono runtime that is used in Unity since it’s very outdated and does NOT use generational garbage collection. It’s an extremely naive GC algorithm that basically just traverses the entire heap when it deems it necessary (it goes to allocate something it doesn’t have space for, for instance), and clears out whatever isn’t referenced, and defrags the heap.
When you enumerate collections, if you use the ‘foreach’ short cut, or anything that wraps the IEnumerable interface in some manner (linq), you’re going to be creating an IEnumerator for storing the enumeration state.
Thing is, because it is all inferred by the IEnumerator interface, and anytime you reference something by IEnumerator, even if the Enumerator state object is a struct, it still gets boxed and put on the heap, because that’s how .Net/mono deals with interfaces.
What I usually do to avoid creating garbage for enumerations is I manually step through it:
var e = myCollection.GetEnumerator();
while(e.MoveNext())
{
//deal with each element via the e.Current property
if(e.Current...) //do stuff
}
See the .Net and Mono frameworks were smart enough to realize this might be a problem, and they type all of their Enumerators as structs. So as long as you have a hard reference to your collection (List vs IList, or Dictionary<T1,T2> instead of IDictionary…) then the Enumerator coming out with be a value type:
No garbage whatsoever when done that way.
You can see me do such things here:
You can even see me using a ‘TempCollection’ to conserve GC for reused collections.
Most GC related optmizations I say don’t bother with unless you’re seeing some hardcore impact in the profiler. Except for collections… collections can get expensive as all heck because they grow in size. Unity even started releasing versions of their functions that take lists/collections to fill rather than spitting back arrays.