Using object pools

Joa Ebert is right when he says that utilizing object pools can make your code perform a lot faster. An object pool is just a container for a bunch of pre-constructed objects that are kept in memory ready for use, rather than being repeatedly allocated and destroyed on demand.
Object pooling makes sense if:

  • you create dozens of short-lived objects in real-time applications like games
  • you need to store and share temporary data throughout complex algorithms
  • the objects are expensive to create (many fields, complex inheritance chain, nested objects)
  • the objects are expensive to remove (unregister listeners, nullify instances)

The only drawback is that memory consumption will raise, but with ridiculously low memory prices this shouldn’t be problem if used wisely ;-)

Implementation

So here is my ObjectPool.as manager class which is an attempt to create a lightweight, fast and reusable solution. The implementation is based on a circular list, and the API is very simple. Download: ObjectPool_v1.0.zip (source, example, asdoc)

EDIT
I have updated the class so it also accepts a factory for object construction.
Download: ObjectPool_v1.1.zip

First, we create the object pool:

var isDynamic:Boolean = true;
var size:int = 100;

var pool:ObjectPool = new ObjectPool(isDynamic);
pool.allocate(MyClass, size);

The isDynamic flag defines the behavior for an empty pool. If true, the pool automatically creates a new bunch of objects for you. If false, the class throws an Error to indicate that the pool is empty. The size value indicates the pool’s capacity – if the pool is dynamic, the pool grows by the initial size each time it becomes empty so it actually never dries up.

By calling the allocate method the pool is filled with 100 instances of MyClass. You can always reuse the pool for another Class by invoking this method again.

If you need to initialize the objects by passing arguments to it, you can do this with a little helper method called initialize:

pool.initialize("funcName", [arg0, arg1,...]);

This goes through every object and applies the function with the given arguments upon each object. This can also be done by reading each object, calling the function and putting it back:

for (var i:int = 0; i < pool.size; i++)
{
	var o:MyClass = pool.object;
	o.init(arg0, arg1, ...);
	pool.object = o;
}

Now to get an instance of MyClass you access the pool like this:

myObjectArray[i] = pool.instance;
//instead of
myObjectArray[i] = new MyClass();

When you are done with your object, instead of throwing it into the garbage collector, you "recycle" it for the next use:

pool.instance = myObjectArray[i];

This assumes that you are storing your instances in an array or something else because if you loose the reference, well it's lost and can't be reused anymore :-) And be careful not to assign the object twice, since then your pool would contain duplicates of the same object!

That's all, pretty simple right ?

Finally, there is the purge() method, which is only interesting for pools that are dynamic. As the pool grows with the demands of the application, it can get quite big. The purge methods scans the pool and removes all allocated but currently unused objects, leaving with a compact representation.

Demo

Here is a little demo which demonstrations how the pool works internally. Actually it's very simple. Pressing the RIGHT arrow key reads an object from the pool (first row), which is then stored in the second row beneath. Pressing the LEFT arrow key gives the object back to the pool. Pressing the ENTER key performs a purge() operation. The purple circle points to the node where the next object is read, the blue circle to an empty node where the insertion is performed.

Performance

Benchmarking revealed that it's always faster to cache instances, even for the generic Object class. All benchmarks were done with the release player 9.0.124 on Vista, an object pool size of 100 and with 100 iterations each to get an average value.

The purple bar indicates the time needed to access the pool:

for (var i:int = 0; i < k; i++) instances[i] = p.instance;

The blue bar measures the task of reading the objects, then putting them back:

for (i = 0; i < k; i++) instances[i] = p.instance;
for (i = 0; i < k; i++) p.instance = instances[i];

The grey bar shows the time needed for creating the objects on the fly:

for (i = 0; i < k; i++) instances[i] = new MyClass();

Here my result:

Caching a native flash Object can be almost 5x faster instead of creating it on the fly.

Almost the same applies to a slightly more complex object like the flash.geom.Point class.

Creating complex objects, here from the flash.display.Sprite class, is extremely slow and up
to 80x faster when pooling.

47 thoughts on “Using object pools”

  1. Excellent blog! I’m about to benchmark your pooling implementation against my own, which is actually based on your single linked list class ;)

    1. I don’t keep the source for the benchmarks..but to compare results both approaches have to run on the same machine, with the same player on the same OS and compiled with the same compiler :-) Also the object pool described here is outdated, but I’ll try to post my new approach soon.

  2. How faster is it to use a linked list instead of an array for containing the pooled objects? Thanks for the post !

  3. Cool idea, love your tutorials! However the price is not the problem today with memory. The problem are the manufacturers who don’t provide 64bit drivers. That fact alone has kept me so far from using 64bit Vista. And 2 Gigs of RAM are pretty quickly fueled … but now I digress.

  4. Nice post!

    I am wondering why you are passing a class directly to the allocate method. I would say a class factory will help you in case the target class should be constructed in a special fashion.

    This would also make the initialize method not needed. You could offcourse implement the allocate method in a way that it can handle factories as well as classes.

    Greetz Erik

  5. I agree with EECOLOR. I think that getting an instance with a getter isn’t clear. I’d prefer using a method that suggest that some work is being done behind.
    But you’ve done an excelent work indeed. Thank you for sharing it.

  6. @luca
    I don’t have exact numbers, but from my experience linked list are always faster, type safe and more elegant.

    @EECOLOR
    My motivation was to provide a very simple one-class solution, so I decided to allocate the pool with a simple class, which should be fine in most cases. But I also agree with you that a factory is a better solution, so I’ve enhanced the class with the ability to use an optional factory if the class is omitted.

  7. Hello,
    It sounds very cool. I download the exemple but I found it difficult. It’s very abstract. Can you post a simple real life exemple using a new Sprite() ?
    Thanks !

  8. This is a nice approach. I usually do object pooling at the Factory level by maintaining a ‘dead pool’. When the factory is asked to make an object, it checks the deadpool to see if an object has been returned there, if so, returns that, otherwise makes a new one.
    But I think the nice thing about Michael’s method is that it lets you do all your expensive instantiation upfront. Does that sound about right?

  9. Pingback: Visual Harmonics
  10. I am trying to use your Pool class and I was wondering what happens with this.

    //—- other variables defined above ——–

    var o : myClass = pool.object;
    myArray[i] = o;

    var bubbleMC: myArray[i];
    blowMC.scaleX += blowMC.scaleX*10;
    pool.object = myArray[i];
    blowMC.x += blowMC+100; // Is blowMC gone?

  11. hey polygonal with the debug player pressing enter on the example for this causes a security sandbox violation error, looks like you left your SOS Tracer commands in and its trying to use a local connection, just thought you’d like to know :)

    all the best

  12. if bubbleMC equals blowMC (otherwise your code doesn’t make any sense) blowMC is still available as long as you keep a reference to it (through myArray)

  13. This looks great! I was looking through the code in version 1.1 and thought I should tell you the method initialize in ObjectPool.as is incorrectly spelled initialze.

    A quick fix for someone when looking through the code but easily overlooked otherwise.

  14. Coolt stuff! I will definitely use this. Will drop you a link if / when it gets implemented

  15. Even though it is obvious that object pooling is advantageous, your documentation and examples are way too cryptic and abstract for anyone to fully understand the product of your hard work. The same goes for linkedlists and doublelinked lists. After all this hard work, it would be warranted that you give some real life examples that are well commented so that the average newbe can come to understand your thought. Remember, good code takes into consideration the dumb people, because the smart ones will either already know or not care.

    Thank you and I hope to see some usable examples soon.

    Ben

  16. I agree with ben. I love all your work, however you’re way more smarter-er than me so, usually the examples you give have their own learning curve.

  17. I’ve been using your ObjectPool class for a little while now and it’s awesome. Thanks! I have one question/observation: If the object you are pooling requires some kind of unique initialization, the ability to grow the pool kind of breaks down. For instance, if you create a pool of objects with grow equal to true and you use up the initial allocation, as soon as the pool expands you theoretically would need to initialize all the new objects similarly. I don’t have a workaround for this and was wondering if you’ve encountered this edge case before?

  18. Hey Michael, I was hoping that you could make your benchmarking source available. I’m currently experimenting with some object pooling in a physics scenario, but would like to see how the numbers for your test match up against my own on a quite a bit different machine.

    I did some of the basic tests describes here (avg. of 100 iterations of 100 object creations) but am coming up with drastically different numbers. So benchmarking against your benchmarks here would be great.

  19. I was going to compare the performance on a few different object pool classes floating around out there. I tried compiling your version 1.1 in CS4 and got the following error:

    1044: Interface method create in namespace de.polygonal.core:ObjectPoolFactory not implemented by class ObjectPool.as$170:SimpleFactory.

  20. am unable to locate a version of polygonal which operates as shown above. the doc’s included with the code have not been enough to help me understand ObjectPool::allocate.

    in my example, I have a function which clones a version of my Class with the correct properties. attempting the following does not even result in the clone function being called:

    pool.allocate(MyClass,model.clone({property:prop}))

  21. Nice objectpool class. Well done.

    Only glitch I found is when a pool is drained (_useageCount == _currSize) and you want to get a next available object (ObjectPool.object) with the pool _grow is ‘true’, the ObjectPool will be filled with new instances of your selected class (or factory), which aren’t initialzed.

    Since you run ObjectPool.initialze() after the ObjectPool.allocation() method, those new objects pushed into the pool aren’t initialized.

    Running ObjectPool.initialize() again, would initialize the previously initialized objects twice.

Comments are closed.