AKA: PLEASE STOP FEEDING THE GC ANIMAL
Given the most probable outcome that we are stuck with the slow garbage collection in Mono 2.6 until Unity 5.0 ( great if we do get an upgrade in 4.x but I don’t hold out much hope ) I’m ruthlessly trying to minimize allocate/destroy to avoid feeding the gc as little as possible. For a while I’ve been doing this in the Folk Tale code - although evidently I still have some work to do looking at ObjectCullingManager - and now I’ve turned my attention to third party components.
The profiler grab below illustrates what we’re up against with components available in the asset store. The figures in red are what is being allocated by the garbage collector. Some components do this every frame, others only do it when the functionality is triggered. While allocation is not necessarily an evil thing, allocating and discarding data for garbage collection every frame is.
VLight is first up. The VLight.OnWillRenderObject is allocating 8-10KB every frame in our particular scene. Looks like it could benefit from some caching.
UnitySteer’s AutonomousVehicle.FixedUpdate is next up, allocating 7.1KB every frame, but that’ll be path data so is expected. I think I’ll actually replace UnitySteer with A*Pathfinding as we no longer have use for it.
A*Pathfinding is allocating sizeable chunks when a path is calculated, but we have a very large grid graph at fine detail, so that’s to be expected. Although some effort to reduce this would be welcomed as we have a lot of characters moving around so it’s fairly regular, and the KB varies depending on the path complexity. Perhaps have a pool of cached path points that is drawn from, and provide a repool function so we can return points that are no longer required?
EDIT ( 30 JULY 2012 ): Aron Granberg has further improved A*Pathfinding, and allocation has dropped to a consistent 21 bytes per frame. It will probably see general release in 3.2.
Extending the investigation another day, it looks like some of Unity’s own code might be contributing to the problem. The CharacterController code for example is allocating 10KB per update cycle:
And here’s what GetComponentsInChildren() allocates. In this instance I’ll have to make sure I cache and reuse the array if possible, preventing it being marked for garbage collection.
I’d really like this thread to serve as encouragement for asset store authors to optimize their code to cache and pool as many objects and variables as possible to minimise any food for the evil gc monster. If other pro user community members are witnessing similar behaviour with other components not listed thus far, please post your profiler data and the function name, and notify the component author. Hopefully we can get more authors to think more about code performance.
WHERE TO OPTIMIZE
The greatest emphasis should be on allocating any reference based heap objects once at the start, and recycling them to avoid feeding the garbage collector. Beyond that, here are a few points to help developers with further optimizations backed by profiler data, but they will have a much lesser impact.
- Recycling heap objects will prevent the garbage collector kicking in
- Recycling is faster than using
new
because it avoids calling the constructor (ctor()
) - Value types are subject to garbage collection when used in classes
- Local variables are significantly faster than member variables
- There is no performance difference between for and while loops
- SqrMagnitude is ever so slightly faster than Vector3.Distance
- For loops are much faster than foreach loops
- Caching Component.transform, .rigidbody, .audio etc is considerably quicker
Disclaimer
These tests have been run on a PC desktop. The results may differ on other platforms and I encourage you to run your own tests.
Update
This thread is now being used to document allocations within the Unity API to assist both the community and UT following on from this topic. If you are thinking of posting a bug here, please read all posts from here onwards to check it hasn’t already been added, and be sure to file a bug report entitled “[Function Name] API call causes c# allocations”.