Pooling

Object pooling is used to decrease the load on the garbage collector.

Pooling is a great way to maximize performance for heavy usage of the system or when running on low-end hardware (e.g., mobile). The Mono Garbage Collector does quite a good job collecting objects that are not used anymore, but one major problem is that every time the GC runs, it needs to freeze the game while it operates. You might have noticed this as small hiccups in your game every few seconds. This is especially problematic on mobile devices with limited RAM and processor power.

One solution to this is to pool most objects. That is, when an object is not used anymore, you put it in a pool. Some time after that, when you need a new instance of the same type of object, you ask the pool if there are any objects in it which can be easily retrieved. Using pools will decrease the load on the GC and thus reduce the number of hiccups in the game.

Path Pooling

Pooling is mostly used with Paths and lists in the A* Pathfinding Project since that is what is allocated the most. Adding pooling logic to your scripts requires very little code, but it can be error-prone if not done correctly.

The pooling in the A* Pathfinding Project is based on a kind of manual reference counting. When you start using a path object, you should call the special function .Claim on it, and when you stop using it, e.g., because it is replaced by a newer path, you should release it with the special Release function. When the release function has been called on a path and no other scripts are using it, it may be recycled at any time. This means that its variables can get reset at any time as well, so after you have called release on a path, you should never keep the reference to it (or at least make sure not to use it again). Also note that the vectorPath and path lists in the path class are also recycled when the path is recycled. So you must be sure not to use them again. If you really want to use only the vectorPath/path variables but recycle the path, you can store it in a local variable and set the vectorPath/path variable on the path object to null, then release the path. Since the variable is null, the path object cannot recycle it, and thus you can continue to use it safely.

If you do not call claim or release on a path, it will simply not be pooled. So if you don't want to bother about pooling, you don't have to. If you only call Claim on a path but forget to call Release and then all your references to it are removed, it will still be garbage collected, but it will not be put back in the pool. If you call Release on a path without having called Claim on it before, or simply call Release multiple times, it will log an error.

Just to give you an example, here is a typical path pooling usage: public class SomeAI : MonoBehaviour {
ABPath path;

public IEnumerator Start () {
while (true) {
GetComponent<Seeker>().StartPath(transform.position, transform.position + transform.forward*10, OnPathComplete);
}
}

void OnPathComplete (Path p) {
// Release the previous path back to the pool
if (path != null) path.Release(this);

path = p as ABPath;

// Claim the new path
path.Claim(this);
}

void Update () {
// Draw the path in the editor
if (path != null && path.vectorPath != null) {
for (int i = 0; i < path.vectorPath.Count-1; i++) {
Debug.DrawLine(path.vectorPath[i], path.vectorPath[i+1], Color.green);
}
}
}
}
Note that when a path is pooled, the vectorPath and path fields on it (and any other lists if the path type had more) will also be pooled. So it is not safe to, e.g., grab the vectorPath from a path object and then pool the path object, but continue using the vectorPath.

The Claim and Release functions take an object reference; it is there mainly to reduce the possibility of user error. Error checking is done so that a path is not released multiple times using the same object, or claimed multiple times.

List Pooling

Maybe not as important for most users, but it might be relevant for advanced users. The project also includes list pooling of generic lists. This is used internally by the system to avoid allocating new lists every time. The list pooling classes are really simple to use. Simply get a reference using Pathfinding.Pooling.ListPool.Claim and when you are done with it, release it using Pathfinding.Pooling.ListPool.Release. The ListPool class takes a type argument, so you can use it with any list type. // Get a reference to a list
List<int> myList = Pathfinding.Pooling.ListPool<int>.Claim();

// Do something with it
for (int i = 0; i < 100; i++) myList.Add(i);
int fiftytwo = myList[52];

// Release it
Pathfinding.Pooling.ListPool<int>.Release(ref myList);
// The 'ref' parameter in the Release call is used to set the myList variable
// to null at the same time to avoid using the list again by mistake.

Debugging

Since pooling might be a bit hard to set up, depending on how complex the scripts are that you use it with, and the results are not instantly verifiable, it can be great to have a debugger. There is one included in the A* Pathfinding Project. Add the script Components → Pathfinding → Debugger to any GameObject.

See

Pathfinding.AstarDebugger for more information

Another very useful thing is to enable the ASTAR_POOL_DEBUG define under the Optimizations tab. If you only have the free version (since Optimizations is a pro-only feature), you can open up the Path.cs file and uncomment the line at the top mentioning ASTAR_POOL_DEBUG.

When this is enabled, every path that is destroyed will log information about whether the path has been pooled or if pooling is used incorrectly.