Trending
Opinion: How will Project 2025 impact game developers?
The Heritage Foundation's manifesto for the possible next administration could do great harm to many, including large portions of the game development community.
This post contains explanation of the garbage collector mechanism together with proposition of an object pooling script, which greatly increases performance of the dynamic scrolled list, which was presented in the last post.
This blog post was previously posted at http://emi.gd/blog/object-pooling/ and was reviewed by Karol Drzymała, head of Tabasco Interactive mobile games studio.
There are many situations when there is a need of spawning and destroying a huge bunch of objects. We could name for example firing bullets, spawning enemies, or – less game-specific – creating large, dynamic lists. (☞゚∀゚)☞ Using Unity’s default mechanisms we could implement such functionality using the Instantiate and Destroy methods. Instantiate creates a new object in memory, and Destroy destroys it. There would be nothing specifically wrong about that, if it wasn’t for the Garbage Collection (GC), which has hands full of work with cleaning all the mess that our bullets leave behind.
GC is a process of automated memory deallocation – thanks to that we don’t have to worry about freeing object’s references by ourselves, thus we are less vulnerable to memory leaks. This is the most common definition (at least for me), but I found a more philosophical one here:Garbage collection is simulating a computer with an infinite amount of memory. Is does so by going through the references graph from the root and checking if these referenced objects reference other ones. If they do, GC marks these blocks of memory as alive (Mark&Sweep algorithm). After the whole lookup, the blocks that are left unmarked are considered free for further allocations. You probably noticed that going through such a huge graph has to have some impact on the CPU usage and you’re right. This is why the memory is allocated in three generations:
• Generation 0 (GEN0) – objects that are recently created, thus there is a good chance that they will be destroyed soon. GC goes through this block most often.
• Generation 1 (GEN1) – when objects are marked as alive several times (the count is dynamically calculated by GC to provide best performance) they probably ale longer lasting ones, so there is no need of checking them s frequently. This is why they advance to the next generation.
• Generation 2 (GEN2) – analogously, memory blocks from GEN1 that are marked as alive several times, advance to this next generation, which is visited by the GC even less often.
Having all of that information leads us to the obvious conclusion – if we create large number of short-lasting elements, we have them all located at GEN0, which means the references graph that is most frequently scanned by the GC is getting bigger. That creates a noticeable impact on the CPU usage.
However, in my case (scrolled list) digging into the profiler’s graph showed that much more significant impact had the operation of instantiating objects. There is a huge pike caused by the GC, but in the constant smaller pikes creating objects took the most of the time. That may be because in Unity, C# objects are just a thin wrappers over the C++ objects, and of course scripts initializing the objects can be costly too.
Rather than constantly creating and destroying objects, it is better to reuse them through the application lifetime.
And that leads us to the final version of the ScrolledList, where the Instantiate and Destroy methods are replaced by methods from this Open Source implementation of an ObjectPoolManager. Of course there also other implementations available in the Internet. This one could use some tweaking, like for example prewarming the prefab (creating a specified number of elements in advance), but generally it’s quite nice.
You May Also Like