Should you always object pool in Unity?
Object pooling is a pattern that's used on many types of software applications, not just games. Whenever something is expensive to initialize and or the birth rate of these objects is high, there's an opportunity for pooling and reusing these objects instead of instantiaing and destroying them as needed.
On games, it is especially useful since we don't want to have a framerate drop because a new bullet or particle effect needs to be instantiated since these things tend to happen in the heat of the moment of a game, a framerate drop would destroy the player's experience. In general, instantiating and destroying objects is way more expensive than just repurposing an inactive object. Another problem that object pooling is used to avoid is the problem of memory fragmentation, briefly explained below. Finally, object pooling can also help to increase data locality, an incredibly important optimization topic for modern (and not so modern) hardware.
But is it a must? Should you always object pool? It's one of those subjects that are always present in the life of the game programmer. Some people take this to an extreme (almost pathological) extent, this article will try to define what is object pooling, when should it be used and most importantly, when it's not that big of a deal and might even create unnecessary bugs.
The pros of pooling
Object pooling is a way to cache (store) objects to later use and reuse them instead of constructing objects from scratch all the time. If the cost of managing this pool and repurposing an object trumps the cost of creating an object from scratch, there's no reason to use an object pool. Some objects are so costly to instantiate, like a database connection, a network socket or a thread, that even if you only need a few of them and their birth rate is low you should still have a pool. Object pooling comes mainly from the mindset of not wasting computations.
Another important aspect of pooling is avoiding heap memory fragmentation. Since you're allocating a big chunk of memory when your game starts, you're not giving room for fragmentation to happen. For more information on the managed heap and how to avoid its fragmentation please see this official Unity tutorial on the subject.
And finally, if you're using an object pool, in most implementations, you're also improving the data locality of your game. Data locality is important because of the way CPUs work. A good explanation is way beyond the scope of this article, but one way to improve the data locality is to try to reuse the object most recently used since there's a greater chance that this object is still in the CPU cache.
- No allocations inside the Update() loop, preventing GC spikes
- Works towards less heap fragmentation
- Better data locality
The main problem with pooling is its complexity, creating and maintaining pools requires a lot of code,
there are several ways in which someone can forget to completely repurpose (reset) the object being
used: internal properties and even relationships to other objects, things that won't ever happen if a
Instantiate was being called. Also, there are always non-obvious cases like coroutines
left running that can create really hard to catch bugs.
Assuming it was correctly implemented, we still have to face the fact that memory is going to be a concern when you start to pool lots of different types of objects—think a hundred, this number depends on the size of your object and the target hardware. So, it's not always a win, it's a tradeoff.
- Might introduce bugs that could be avoided by just calling
- Potentially big memory footprint
A simple rule of thumb is: by default, use a pool for projectiles and particles only. Think in pooling other objects if the GC is kicking in during the action of your game.
Now you have the tools to choose whether an object pool is appropriate or not, this is always an important decision.
If that article was useful and you still want to be in touch, you can find me on [email protected] for questions, opinions or anything else, I'm pretty flexible.
- object pooling