Pooling
Object pooling is used to decrease the load on the garbage collector.
Pooling is a great way to maximize the 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 the objects which are not used anymore, but one major problem is that every time the GC is run, it needs to freeze the game while it runs. You might have noticed this as small hiccups in your game every few seconds. Especially on mobile with limited ram and processor power this is a problem.
One solution to this is to pool most objects. That is, when an object is not used anymore you put it in a pool and 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 got easily. Using pools will decrease the load on the GC and thus reducing 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: [HelpURL("https://arongranberg.com/astar/documentation/beta/class_some_a_i.php")]
Note also that when a path is recycled, i.e put back in the pool after it has been released, the vectorPath and path variables on it (and any other lists if the path type had more) will also be recycled. So it is not safe to grab the vectorPath from a path object and then recycle the path object but continue using the vectorPath.
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 any previous paths
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);
}
}
}
}
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 does 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.Util.ListPool.Claim and when you are done with it release it using Pathfinding.Util.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.Util.ListPool<int>.Claim();
// Do something with it
for (int i = 0; i < 100; i++) myList.Add(i);
int fiftytwo = myList[52];
// Release it
Pathfinding.Util.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 if the path has been pooled or if pooling is used incorrectly.