Skip navigation

Javascript

Disclaimer: this is a technical article without fancy images.


I can’t help but find Javascript spectacularly unsuited to game development.

Think about it: no integers, no 32-bit floating points, overall slowness, uncertainty, and the dynamic side of the langage gets it the way of JIT optimization. When you come from C++ and D it feels like a huge downgrade, and yes I did read Javascript: The Good parts. The features you need are not here while those you can’t use are plenty.

Moreover, the usual guidelines promote a programming style which favors poor performance. The Canvas rendering is slow but physics or AI can quickly become another bottleneck in your game if you rely on best practices.

Luckily you can adapt your code to help the JIT do a better work. This is a domain where I think premature optimization pays off. Using the Firebug profiler I chose to optimize everything in a consistent way.

Note that this performance guide is based on Firefox 3.6 and the optimizations presented here might be specific to this browser.

Objects

Objects worked better for me when created like this:

var C = function(x, y)
{
// initialize members, do stuff
};

Then assigning the prototype:

C.prototype = {
method1: function()
{
// do something
}
method2: function()
{
// do something else
}
};

Edit: it’s not the one true way, see this jsperf test to make your own measurements (thanks @kuvos).

Allocations

The problem with allocating memory is that it stresses the GC and provokes annoyingly long pauses in your game. I mean several frames being skipped where a stable FPS is your goal.

Consequently, there is no single new in the Crajsh game loop. The classic recipes apply: pools, FIFO, stacks, arrays. The pauses sometimes still happen though, because I allocate stuff for each new game.

Note: some operations seem to do hidden allocations, eg. drawing a canvas into another with a different size with Firefox.

Cache members, break encapsulation

I cached properties by hand anywhere possible. Member access is slow in Firefox 3.6 so it’s crucial to get them out of critical loops. If you inline some functions by hand, you’ll be able to cache even more property access. I did so.

I also replaced most of getters by direct member access. If you prefix all members with an _underscore it’s still easy to change a member name. This is less work for the JIT and less code.

Symbolic numerical constants can be replaced by a literal like this:


var a = /* tron.MY_CONSTANT */ 4;

That way you can still grep for it and your code is a bit harder to decipher.

Closures

I don’t understand why, but accessing to a closure (not just creating) introduces a slowdown in Firefox 3.6. The symptom is GC pauses. I’ve worked around the problem by removing all closures from my code. The JIT could theorically optimize closures but it doesn’t happen.

The Prototype.js bind function can help you to eliminate even more closures from your code. I might be wrong but I did see a speed-up.


Function.prototype.bind = function()
{
var fn = this, args = Array.prototype.slice.call(arguments), object = args.shift();
return function()
{
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};

Arrays

Array literals caused pauses in Firefox 3.6 much like closures and allocations do. It’s sometimes better to have a string literal and then convert it to an array.

I create all arrays with a sufficient size. No resizing happen in the game loop. Then, the bulk of the processing can be done by iterating on arrays, not object properties.

I also think it’s better to use monomorphic parameters and variables. Make sure the JIT know the type of each value where they are used. You’ll see a speed-up if you fill your arrays upfront with the right type, don’t let them undefined.

tl;dr: if you want good performance with Javascript, use a static subset of the language.

Optimizing Crajsh – Part 1 – Rendering

Advertisements

8 Comments

  1. Heh, seeing js from c coders always jumps to the eye (in js, the opening curly brace is not usually put on a seperate line). But I’ll refrain from further style comments.

    But what struck me as interesting is that you used the prototypal inheritance for the sake of speed. Even though object creation is faster (which makes sense because in the jsperf case, the prototype step happens in all three cases, the other two cases simply do more stuff afterwards), lookups are more expensive because they have to traverse the prototypal chain.

    For instance;

    var x = 5;
    var A = function(){};
    A.y = 5;
    A.prototype.z = 5;
    var a = new A;
    a.k = 5;

    Now, there are now four different ways of getting 5. x will be fastest, since local variables simply are. Then come own properties, which means A.y and a.k. These are equally fast (they are both “own” properties of the object). And then, much further away, comes a.z. Because first it checks whether a has an own property “z”, which it doesn’t. Then it asks for the prototype object. It checks whether the prototype object has an own property “z”. Etc.

    I still prefer the prototypal way over anything, but when speed really does matter to this level of detail, I could see why you’d want to just use unclassified object literals.

    As for the jsperf case, it’s kind of meaningless for your objective; the cost of creating an object is one time, where as property access is something you’ll be doing the rest of the time.

    About the getters and setters. Function execution is dead slow in js. Avoid it if you can. That’s why getters and setters dont work. That’s why you inline them. Not sure if that was obvious, sorry if it was.

    It strikes me as odd that an array literal [] would cause pauses while converting a string to an array doesn’t. It may have been what you’ve observed, but it really shouldn’t matter and it’s very unlikely to be a stable factor across the board (yeah, I know, fx36).

    Closures suffer from the same problem as prototypes, except it’s the scope chain.

    var x;
    function f(y){ x; y; }

    the read to x is slower than the read to y, because y is a local variable and x is one level higher in the scope chain. Also, it’s good to remember that, if you’re keen on memory management, _everything_ in an outer scope is retained as long as there’s an inner scope (with access to that outer scope) that has not been resolved. In the above case, x may only perish when f perishes.

    Hope I didn’t step on too many toes and I am sorry if I’m just saying stuff you already knew 🙂

    • Hi Peter,

      Indeed the linked jsperf test is not the right one. I’m pretty sure there is a test somewhere with just member access but couldn’t find it.

      I believe optimized code does not follow a prototypal chain when a property is accessed. I assumed objects would be “flattened” at some point by the JIT.

      Actually I followed what was faster. First I had members in closures:

      function C() {
      var _member = 0;
      this.method = function() {
      return _member;
      }
      }

      Then I eliminated the closure:

      function C() {
      this._member = 0;
      this.method = function() {
      return this._member;
      }
      }

      Then I used the prototype:

      function C() {
      this._member = 0;
      }
      C.prototype.method = function() {
      return this._member;
      }

      Then I assigned the prototype with an object literal:

      function C() {
      this._member = 0;
      }
      C.prototype = {
      method: function() {
      return this._member;
      }
      }

      And performance went up all the way. I didn’t try plain object literals though.

      Didn’t know about the K&R style in Javascript. 🙂 I suppose it’s to be consistent and avoid the object literal mistake.

  2. The revealing pattern (what you call members in closures) is too costly, for sure. Maybe unintentional, but you do realize you can just access this._member directly right? Eliminating the member function (if it’s just an accessor, of course).

    What you could try is to explicitly copy everything from your prototype object into the new object directly. That way they’ll be own properties and, in theory, be faster. The overhead is a one-time cost at creation time.

    var F = function(){
    for (var key in F.prototype) if (F.prototype.hasOwnProperty(key)) this[key] = F.prototype[key];
    };

    I wouldn’t be surprised if that resulted in (slightly) better net perf.

    And, if your architecture allows it, I’m positive plain objects will be even faster. Especially if you continuously spawn `new` objects (but that’s probably not the case..?).

    And another reason for k&r is that you’ll be less inclined to make “return statement” mistakes. Js won’t allow a newline between return and the value to return (due to automatic semicolon insertion). If you do, it’ll insert a semi right after the return and return “undefined” instead. Same for throw, continue and break, but you’re not as likely to run into a problem there. Returns are allowed inside the value (eg, as long as the expression to return _starts_ on the same line as the return keyword, it’s ok).

    I like my js compact and to the point 🙂

  3. Also, not using the Prototype might be useful for member Lookups, but it increases the memory by an order of magnitude. And this seems to be, at least to me, a lot bigger problem when you really want to develop Games, and not some little nice 3k Script.

  4. That argument only flies if you create thousands and thousands of objects. Which might be true in some games, but it shouldn’t be the case for crasjh. But yeah, that’s how prototype works, it shares those resources 🙂

  5. OK guys, I was a bit unsure so I wrote a jsperf test: http://jsperf.com/closures-vs-prototype-1-speed/2

    I chose method E for Crajsh because FF 3.6 was the worse performing target browser so I wanted to optimize for it. Draw your own conclusions.


One Trackback/Pingback

  1. […] Ce billet était mentionné sur Twitter par Scott Koon, Kyle Simpson et Mike Taylor, #ponce. #ponce a dit: Optimizing Crajsh – Part 2 – Javascript http://bit.ly/gLrjlt […]

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

%d bloggers like this: