No more garbage : Pooling objects built with constructor functions – verbose version –

Why creating an object creates an issue.

We all know about the evil garbage collector of Javascript, a cruel monster that might at any moment freeze your game to recollect unused memory, and waste some frames or loose some inputs events, spoiling the game experience.
The fight against garbage creation must be led on several fronts : I’ll adress here the specific case of object creation using a constructor function, and in this article, i’ll present a way to recycle those object in a simple manner.

For those who likes to know the end of the movie before it starts : The simple and classical pattern i expose here leads to X2 to X5 performance boost, and reduces game freeze.

You can find the code of this article here :

https://github.com/gamealchemist/Javascript-Pooling/tree/master/True_JS_Classes )

Objects Built with a constructor function

Using  a constructor function looks like :

var MyClass = function (param1, param2, ...) {
    this.prop  = param1;
    this.prop2 = param2;
};

MyClass.prototype.method1 = function(..) { ... };

var myObject = new MyClass(param1, param2, ...);

For the creation of myObject, the memory system is used three times :

  1. using the new operator is equivalent, in Javascript, to create a new object : {}, and assigning it to the ‘this’ of the constructor function.
  2. The constructor function (MyClass) will then perform intialisation and add some properties to the object. Memory will again be allocated for those properties.
  3. When the object is no longer in use (‘goes out of scope’), its memory it is not recollected at once : rather it is marked for recollection, hence feeding the evil garbage collector. When, at some random point in time, memory is lacking, a large amount of memory is reclaimed all at once : garbage collection occurs, freezing the game for up to 10ms – a disaster-.

Let us see a very simple example :

// in the init of the game...
var someBadGuy = new BadGuy() ;

// later on during a fight...
if ( Hero.strength > someBadGuy.strength ) {
   Hero.kills++;
   someBadGuy = new BadGuy(); //create a new enemy to fight against
}

Here, when we create a second bad guy after defeating the first one, we overwrite the reference to the first guy  (someBadGuy= new BadGuy()), so we have no more reference to the first guy : We created garbage.
Hence the first guy object is marked to be later recollected : this simple code is allready exhibiting a potential issue (in fact it created a performance issue in the future).

The performance hit due to object depends on two factors : the frequency of creation/disposal of objects, and the size of those objects.
Some sensitive cases might be :

  • Frantic games (no, i’m not talking about Super Crate Box 🙂 )
  • Games using a particle engine (for explosions, rainbows, …).
  • Games using 2D/3D physic engine(hence vectors computations).

But even in a slow-paced game, the recollection will randomly occur and make your character sometimes react oddly to, say, keystrokes, and hinder player’s experience.

Another thing worth noticing : Some (most?) game frameworks generates quite some garbage only to handle mouse moves or key strokes, and/or to handle object collisions, and/or use a class system that makes any class instance -even for simple classes- use quite some memory.
So before you even started to put some action, you can rest assured that the garbage collector will score in your game.
Still, this is not a reason to quit the fight.

The solution : let’s go to the pool

There is a much neater way to handle your objects : use a pool.

A pool is a stack of object that you put aside for your game :

  • When a new object is required, just take it from the pool then initialize it.
  • When you no longer use your object, throw it back in the pool.
  • If you need an object and the pool is empty, then -too bad- just use standard new() to get a new one.

So allocations happens less frequently, and recollection never happens (during the game).
Even better : if you determine the maximum number of objects your game might use, you can even pre-fill your pool with this number of objects, and you’ll never have to create or wait for recollection for this object.

How nice !

So how do we implement this in Javascript ?

Preliminary remark : I’ll make use here of ‘true’ Javascript ‘classes’,
i.e. classes :

  • setting properties in the constructor.
  • defining methods on the prototype.

This is the fastest and most memory efficient way to create objects in JS, and objects created this way also have the best performances.

Second remark : I won’t adress how to handle pooling with any of the many class library available for JS (like JQuery’s class system).

So here’s a (very simple) example of such a true JS class :

var BadGuy = function(posX, posY, gun, ammo) {
      this.posX   = posX  ;  this.posY   = posY  ;
      this.speedX = 10    ;  this.speedY = 0     ;
      this.gun    = gun   ;  this.ammo   = ammo  ;
      this.isAlive  = true  ;
      };

BadGuy.prototype.move = function(dt) {
      this.posX += this.speedX * dt;
      this.posY += this.speedY * dt;
};

BadGuy.prototype.canShoot = function() {       
       return (this.ammo != 0);
};

(yes, very simple 🙂 )
And you use it with :

// create a new bad guy like this :
var myBadGuy = new BadGuy( 100, 10, 'AK47', 100);

// later on you can use it like this  :
if (myBadGuy.isAlive && myBadGuy.canShoot()) {
      Hero.runAwayShouting('please do not use your '
                                  + myBadGuy.gun +' on me !!');
};

So now let’s get back to pooling : the thing you have to change to a true JS constructor function to enable pooling is to handle a call with no arguments and make sure you setup your object just like a brand new one within the constructor :

var BadGuy = function(posX, posY, gun, ammo) {
      this.posX   = posX || 100 ;  
      this.posY   = posY || 0   ;
      this.speedX = 10    ;  this.speedY = 0        ;
      this.gun    = gun  || 'M16'   ;  
      this.ammo   = ammo || 100     ;
      this.alive  = true  ;
      return this;
      };

This not a limitation, since even if you’re not pooling, handling undefined arguments :

  • Protect your function from calls to new() with a wrong number of arguments.
  • Ensures inner JS engine optimisations, since properties always have the same inner type (they never have the doomed ‘undefined’ value ).
  • It allows you to call the constructor with only the relevant parameters.

What you mustn’t do when pooling, though, is to add properties or methods on a created object, like with :

myBadGuy.isSmiling = true;

This, again,  is not a limitation, since late object change is a bad practice : it breaks inner optimisations of the JS engine (The JS engine creates a cached backing class for your object : this cache is broken if you change the object on the go).
– Simple solution for this : pour all the stuff you need in your object in the first place -.

So let us now create our pool :

BadGuy.pool = [];  // this one was hard.
(this code, as well as following code, has to be inserted
before the first object creation, but i don’t copy everything for clarity).

Now to get a new bad guy, we need another method that retrieves an object from the pool if one is available : pnew :

 BadGuy.pnew = function( posX, posY, gun, ammo) {
         var newGuy = null;
         // use the pool if object available... 
         if (this.pool.length >0) {
              newGuy = this.pool.pop();
          } else {
          // ... or create a new object if pool is empty.
              newGuy = new BadGuy();
         }
         // initialize and return object.
         BadGuy.apply( newGuy, arguments );
         return newGuy;  
};

Use the pnew function like this :

var myBadGuy = BadGuy.pnew(100, 50, 'M4', 90);

Now, to dispose of the object, we also need a specific method that throws back the object on the pool : pdispose :

BadGuy.prototype.pdispose = function() {
     this.pool.push(this);        
};

Notice that this has to be set on the prototype (it is an instance we are disposing), while pnew is set on the creator function.
use the pdispose function like this :

if ( myBadGuy.health <= 0 ) {
     myBadGuy.pdispose();
     myBadGuy = null    ;
}

Notice that i set the myBadGuy var to null after disposal : this is to ensure we never reference twice the same object.
Here’s an example of what might occur if we do not pay attention :

var firstBadGuy = BadGuy.pnew(10); // get a guy, posX=10
firstBadGuy.pdispose();            // finally we don't need him...
                                   // ... but keep the reference..

var secondBadGuy = BadGuy.pnew(50); // get another guy posX=50 
                                    // It is taken from the 
                                    //  pool, and it is the latest
                                    // pushed, so === to firstGuy

// so now we have two references to the same object.
// if we do :
firstBadGuy.posX = 200 ; 
// we have for the second guy :
console.log( secondBadGuy.posX ); // --> output is 200, not 50 !!!

The issues that might be caused by multiple references might be a nightmare to detect and debug : each time you call pdispose(), ensure you don’t hold any reference to the disposed object.

So now, you must change all your new() calls to pnew(), and whenever an object is no longer in use, you must take care of pdisposing it and clearing any reference to it.

and then …

HURRA !!!!

At this point we have our pooling system, and the ugly garbage collector is defeated !!

or is he ?

Not quite, i am afraid : we have two concerns left.

Issue 1 : We still create some garbage.

When using push() and pop(), we do allocate/disallocate memory, so we still feed the monster with some crumb, so let us use an always-growing stack by handling the lenght separatly.  You’ll see how in the final code.

Issue 2 : what about re-use ?

We obviously need a way to avoid re-writing the same code for each pooled class.
Let us define a setupPool function on the prototype of the Function object, so that all functions call get pooled easily.

Object.defineProperty(Function.prototype,'setupPool', { value : setupPool });

function setupPool(initialPoolSize) {
	if (!initialPoolSize || !isFinite(initialPoolSize)) throw('setupPool takes a size > 0 as argument.');
    this.pool                = []          ;
    this.poolSize            = 0           ;
    this.pnew                = pnew        ;
    Object.defineProperty(this.prototype, 'pdispose', { value : pdispose } ) ; 
    // pre-fill the pool.
    while (initialPoolSize-- >0) { (new this()).pdispose(); }
}

function  pnew () {
    var pnewObj  = null     ; 
    if (this.poolSize !== 0 ) {              
// the pool contains objects : grab one
           this.poolSize--  ;
           pnewObj = this.pool[this.poolSize];
           this.pool[this.poolSize] = null   ; 
    } else {
// the pool is empty : create new object
           pnewObj = new this() ;             
    }
    this.apply(pnewObj, arguments);           // initialize object
    return pnewObj;
}

function pdispose() {
    var thisCttr = this.constructor  ;
    if (this.dispose) this.dispose() ; // Call dispose if defined
    // throw the object back in the pool
    thisCttr.pool[thisCttr.poolSize++] = this ;   
}

A few comments on this code :

  1. As told earlier, i use an always-growing array/stack, so i handle its length separately in a poolSize property.
  2. I added here the possibility to pre-fill the pool, so no object creation occurs even within the first seconds of the game.
  3. I set, within the pool, to null the reference of the object we just grabbed to allow recollection in case it is not pdisposed afterwise, and just goes out of scope.
  4. In case you have some disposal work to do, just set a dispose() method on your object, it will get called when pdispose is called. One reason to do so might be that the object’s properties themselves are pooled.
  5. You might want to detect if you forget to pdispose some objects by counting all calls to pnew and to pdispose. At any moment you should have :
    activeObjectsCount == pnewCount  – (pdisposedCount – initialPoolSize)

Use setupPool like this :

BadGuy.setupPool(100);
// to create an instance :
  var myBadGuy = BadGuy.pnew(20,... );
// to dispose of it :
   myBadGuy.pdispose();

Reminder : the constructor function must handle undefined parameters and fully initialize the object.

So now victory is complete and all this was a very nice fight. Thank you for reading.

No ! Come On ! We want to know about the performance boost !

Ho, yes.

sure.

In fact this quite difficult to build a test that reflect an actual game sequence, where many object of different sizes have very different lifespan, and where ‘standard’ object ({}, [], strings, closures, functions, …) might be forgotten as garbage all along the way by many not-so-efficient game frameworks.
Another thing is that the performance measure won’t show us for how long the garbage collector froze the JS code during the tests. But i watched on FF/Chrome and we can see that garbage collection occurs often without pooling, and never when pooling (as expected).

Anyway i built a test in JSPerf that reflects *somehow* the performance gain you can expect from pooling.

So i consider that we have 4 different pooled objects, with different memory footprint (more realistic), and a given number of active object that i select randomly within those 4 classes.
We’ll create/dispose of them using three methods :

  • standard way : using new / relying on garbage collection.
  • using an empty-at-first pool.
  • using an pre-filled pool.

You can see the test and experiment by yourself at http://jsperf.com/pooling-test/6

I took 100 active objects, and 50000 of them will be created/disposed randomly.

Here is a screenshot of the results for :
Win8 Chrome / mac OS FF / win 8 IE10 / ipad Safari / mac OS Safari :

pooled vs non-pooled performance
pooled vs non-pooled performance


We can see that :

pre-filling the pool helps either a little or not at all.
Chrome sees a near X2 boost.
Firefox manages poorly memory, bust can be as fast as Chrome if helped : X5 boost.
IE10 gets a X3.3 boost.
The ipad appreciates the pre-fill, and gets a X2 boost.
Safari on mac OS, also enjoys pre-fill X2 boost.

Let me know if you experiment pooling in your game : i especially think here of games using intensively vector computation, where the speed boost should be tremendous.

I hope you enjoyed reading this article, and i wish you a good game.

Advertisements
This entry was posted in Uncategorized. Bookmark the permalink.

One Response to No more garbage : Pooling objects built with constructor functions – verbose version –

  1. Pingback: No more garbage : Pooling objects built with constructor functions | gamealchemist

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s