Object Pooling

Object pooling is an optimization pattern. It's used to improve performance, in certain cases, by re-using objects instead of allocating memory for them on demand. In C/C++, one the things object pooling has to offer is a solution to avoid memory fragmentation. In C#, we don't have to worry about memory fragmentation thanks to garbage collection. However, garbage collection can be still be too expensive for certain parts of real-time applications, especially on mobile devices with slower CPUs and simpler garbage collectors. More details on object pooling here.

IMPORTANT

Always profile the game for performance problems! Using a Pool<T> without first profiling for the need of one may result in a decrease in performance in certain cases. If you are unsure, don't use the object pooling pattern.

Creating a Pool-able Object

All objects which can be pooled need to implement the IPoolable interface. The following is a code snippet with comments demonstrating how to implement the interface.

private class MyPoolable : IPoolable
{
    private ReturnToPoolDelegate _returnAction;

    void IPoolable.Initialize(ReturnToPoolDelegate returnAction)
    {
        // copy the instance reference of the return function so we can call it later
        _returnAction = returnAction;
    }

    public void Return()
    {
        // check if this instance has already been returned
        if (_returnAction != null)
        {
            // not yet returned, return it now
            _returnAction.Invoke(this);
            // set the delegate instance reference to null, so we don't accidentally return it again
            _returnAction = null;
        }
    }
}

Using Pooled Objects

Creating a Pool

Instantiating a Pool<T> is similar to any generic collection, i.e List<T>, but the pool does require 2 parameters for it's constructor. T also has to implement IPoolable.

var pool = new Pool<MyPoolable>(50, index => new MyPoolable());

The first parameter is the capacity of the pool; the maximum number of object instances the pool has reference to. The second parameter is the delegate responsible for creating each object instance.

NOTE

Having too large of a capacity will waste memory, but having too small of a capacity will limit the number of object instances that can be pooled.

All object instances are created when the pool is instantiated.

Getting a Pooled Object

A free pooled object instance can be requested from the pool instance.

var myPoolable = pool.Request();

NOTE

If the pool is empty, the result will be null.

Returning a Object to the Pool

When the object instance is no longer needed it should be returned to the pool so it can be re-used.

myPoolable.Return();