A first try at object pooling

As I mentioned in my previous post, I started experimenting with object pooling. To start off with I didn’t worry about performance at all - I just wanted to have a functional way to use entities without repeatedly creating or destroying them. It took a couple of days, but I finally have a working version that (so far) does what I need.

I knew that the main functionality I’d need would be the following:

So the cases where I would normally use the engine’s spawnEntity() would now be replaced by entity activation and the cases where I’d normally use the engine’s kill() are now replaced by entity deactivation instead of outright destruction.

I ended up doing it like this. I’m not sure how many disclaimers I have to give here, but remember that I do not yet know enough about the ins and outs of Garbage Collection and performance to claim that this is a great, or even an acceptable, way to implement something like this. All I know is it works - as in, my game does not crash horribly.

Spawning entities

The first thing I do when the level starts is spawn 40 flea entities and 5 Babyflea entities:

for (var i = 0; i < 40; i++) {
    this.spawnEntity(EntityFlea, 0, 0, {inUse: false});
    if (i < 5) {
        this.spawnEntity(EntityBabyflea, 0, 0, {inUse: false});
    }
}

The entity instance then gets added to the object pool in the init function.

ig.game.pool.addToPool(this,ig.game.pool.allEntitiesArr);

The only other things that happens in this init function is the creation of objects that will be required for every instance of this entity - mostly timers in my case, but also animation definitions. I have moved all other actions that would normally happen when I saw an entity into a new function called initialize().

Reasoning: Initialize all objects on entity spawn, then simply reset and recycle them as you reuse each instance of the entity without having to create and destroy more objects.

The actual pool

The pool is initialized on game load. In the pool code itself I defined specific arrays for each kind of entity that I want to store. Eventually I can make this a little more dynamic and create these arrays on the fly, allowing for easier reusal of the boilerplate pool code across projects. Right now, I am storing two kinds of entities and have two separate pool arrays for both.

allEntitiesArr: [],
allEggsArr: [],

The addToPool() function that is used in the entity’s init function simply puts the entity into the beginning of the specified array:

addToPool: function(entity,arr) {
    arr.unshift(entity);
},

When I need to use an entity from the pool, I use the useObject() function:

for (var i = 0; i < 5; i++) {
    this.pool.useObject('flea',{kind: 'male'});
    this.pool.useObject('flea', {kind: 'female'});
}

The problem with this is that, from what I understand, that {kind: 'male'} code creates a brand new object that will then need to be cleaned up via garbage collection. There can be other attributes here, too (like spawn position, for example), and all of these will be new objects being created. Am I wrong or is this another inefficiency?

Using an object from the pool

Anyway, continuing on - in the pool code, here is what happens when useObject() is called:

useObject: function(object,attributes) {
    // Set poolArr and entityType depending on which entity is being used
    var poolArr = null;
    var entityType = null;
    var entity = null;
    switch(object) {
        case 'flea': 
            poolArr = this.allEntitiesArr;
            entityType = 'EntityFlea';
            break;
        case 'egg':
            poolArr = this.allEggsArr;
            entityType = 'EntityBabyflea';
            break;
    }
    entity = poolArr[0]; // Get first entity in relevant pool

    // If the entity is not already in use...
    if (entity.inUse === false) {

        // Set any additional attributes
        for(var propt in attributes){
            entity[propt] = attributes[propt];
        }

        entity.initialize(); // Initialize entity
        entity.inUse = true;
        this.moveArrElement(poolArr,0,poolArr.length - 1); // Move the now used entity to the end of the pool
    }

    // If the entity IS already in use, either the array is cluttered or there are no free entities left in the pool...
    else {
        var foundAvailableEntity = false;
        // Loop through pool backwards
        for (var i = poolArr.length - 1; i > 0; i--) {
            entity = poolArr[i];
            // If the entity is not in use, move it to the front of the array
            if (!entity.inUse) {
                this.moveArrElement(poolArr,i,0);
                foundAvailableEntity = true;
            }
        }

        // If no available entities were found, spawn a new entity.
        if (!foundAvailableEntity) {
            ig.game.spawnEntity(entityType, 0, 0);
        }
        this.useObject(object,attributes);
    }
},

When removing an object, I simply reset relevant attributes in each entity’s destroy()  function and set inUse to false,  freeing the object up for reuse down the line.

Results

The results thus far are suspicious. I wish I had done some better recording of my _original _performance, but basically system lag was pretty high and while the game ran semi-smoothly most of the time, there were a lot of very visible negative spikes, resulting in a framerate between 35 and 60.

I now start the game with 50-55 entities present at all time. The ImpactJS debug console shows minimal system lag and the FPS is consistently sitting between 55 and 60. However, the timeline in Chrome DevTools does still show garbage collection spikes…

I’m very skeptical and hesitant to attribute the sudden improvement in performance to the object pool just yet. The pool implementation is very rough at best - I don’t know if it can account for such a drastic change. Thoughts?

[caption id=“attachment_49293” align=“aligncenter” width=“900”]ImpactJS debug menu - post object pool ImpactJS debug menu - post object pool[/caption]

comments powered by Disqus