Jump to content

  • Log In with Google      Sign In   
  • Create Account

Interested in a FREE copy of HTML5 game maker Construct 2?

We'll be giving away three Personal Edition licences in next Tuesday's GDNet Direct email newsletter!

Sign up from the right-hand sidebar on our homepage and read Tuesday's newsletter for details!


We're also offering banner ads on our site from just $5! 1. Details HERE. 2. GDNet+ Subscriptions HERE. 3. Ad upload HERE.


Like
23Likes
Dislike

Writing Fast JavaScript For Games & Interactive Applications

By Mohsen Heydari | Published Dec 15 2013 12:51 PM in Game Programming
Peer Reviewed by (jjd, jbadams, apatriarca)

performance optimization javascript

Recent versions of JavaScript engines are designed to execute large bodies of code very fast but if you don't know how JavaScript engines work internally you could easily degrade your application's performance. It's specially true for games that need every drop of performance they can get.

In this article I will try to explain some common optimization methods for JavaScript code that I picked up during development of my projects.

Garbage Collection


One of the biggest problems in having a smooth experience resides in JavaScript garbage collector(GC) pauses. In JavaScript you create objects but you don't release them explicitly. That's job of the garbage collector.

The problem arises when GC decides to clean up your objects: execution is paused, GC decides which objects are no longer needed and then releases them.

Attached Image: memory usage chrome.JPG
Zig-zag memory usage pattern while playing a JavaScript game.


To keep your framerate consistent, you should keep garbage creation as low as possible. Often objects are created with the new keyword e.g. new Image() but there are other constructs that allocate memory implicitly:

var foo = {}; //Creates new anonymus object
var bar = []; //Creates new array object
function(){}  //Creates new function

You should avoid creating objects in tight loops (e.g. rendering loop). Try to allocate objects once and reuse them later. In languages like C++ sometimes developers use object pools to avoid performance hits associated with memory allocation and fragmentation. The same idea could be used in JavaScript to avoid GC pauses. Here you can find out more about object pools.

To better demonstrate implicit garbage creation, consider following function:

function foo(){
	//Some calculations
	return {bar: "foo"};
}

Each time this function is called it creates a new anonymous object that needs to be cleared at some point. Another performance hit comes from using array shorthand [] to clear your array:

var r = new Array("foo", "bar"); //New array filled with some values, same as ["foo", "bar"]
r = [];//Clear the array

As you can see, the second line creates a new array and marks the previous one as garbage. It's better to set the array length to 0:

r.length = 0;

Functions can wake up the GC as well. Consider the following:

function foo(){

	return function(x, y){
		return x + y;
	};
	
}

In above code we return a function refrence from our foo function but we also allocate memory for our anonymous function. The above code could be rewritten to avoid GC:

function bar(x, y){
	return x + y;
}

function foo(){
	return bar;
}

An important thing to be aware of is that global variables are not cleaned up by the garbage collector during the life of your page. That means objects like the above functions are only created once so whenever possible use them to your advantage. Also globals are cleaned up when users refresh the page, navigate to another page or close your page.

These are straightforward ways for avoiding performance hits that come from GC but you should also be aware of other JavaScript library functions that may create objects. By knowing what values are returned from your library functions you could make better decisions about designing your code. For example, if you know that a library function may allocate memory and you use that function in a performance-critical section of your code you may want to rewrite or use a similiar but more effecient function.

JavaScript Internals


JavaScript engines do some preparation on your code (including some optimizations) before execution. Knowing what they do behind the scenes will enable you to write generally better code. Here is an overview of how two popular JavaScript engines (Google's V8 and Mozilla's SpiderMonkey) work under the hood:

V8:
  • JavaScript is parsed and native machine code is generated for faster execution. The initial code is not highly optimized.
  • A runtime profiler monitors the code being run and detects "hot" functions (e.g. code that runs for long time).
  • Code that's flagged as "hot" will be recompiled and optimized.
  • V8 can deoptimize previousely optimized code if it discovers that some of the assumptions it made about the optimized code were too optimistic.
  • Objects in V8 are represented with hidden classes to improve property access.
SpiderMonkey:
  • JavaScript is parsed and bytecode is generated.
  • A runtime profiler monitors the code being run and detects "hot" functions (e.g. code that runs for long time).
  • Code that's flagged as "hot" will be recompiled and optimized by Just-In-Time(JIT) compiler.
As you can see, both engines and other similiar engines applies common optimizations that we can take advantage of.

Deleting Object Properties


Avoid using the delete keyword for removing object properties if you can. Consider the following code:

var obj = { foo: 123 };
delete obj.foo;
typeof obj.foo == 'undefined' //true

It will force V8 to change obj's hidden class and run it on a slower code path. Same is true about other JavaScript engines that optimize for "hot" objects. If possible, it's better to null the properties of object instead of removing them.

Monomorphic Variables


Whenever possible try to keep your variables monomorphic. For example don't put different objects with different hidden classes in your arrays. Same applies to properties and function parameters. Functions that are supplied with constant parameter types perform faster than the ones with different parameters.

//Fast
//JS engine knows you want an array of 3 elements of integer type
var arr = [1, 2, 3];
//Slow
var arr = [1, "", {}, undefined, true];

The below function can be called with different parameter types(ints, strings, objects, etc) but it will make it slow:

function add(a, b){
	return a + b;
}

//Slow
add(1, 2);
add('a', 'b');
add(undefined, obj);

Array of Numbers


Using an array is usually faster than accessing object properties. This is particularly beneficial when the array contains numbers. For example it's better to write vectors using arrays than with objects with x, y, z properties.

Arrays with Holes


Avoid "holes" in your arrays. It will make things slower than it should. Holes are created by deleting elements or adding elements out of range of the array. For example:

var arr = [1, 2, 3, 4, 5];//Full array
delete arr[0];//Creates hole
arr[7] = 1;   //Creates hole
var hArr = [0, 1, 2, 3, /* hole */, 5];//Holey array

Pre-allocating Large Arrays


Current implementations of SpiderMonkey and V8 favor growing over pre-allocating large arrays (with more than 64K elements). Keep in mind that this is largely implementation-dependent as some implementations such as Nitro(Safari) or Carakan(Opera) favors pre-allocated arrays.

Object Declaration


I can't give you single method for creating objects as it's very engine-dependent but you can see current performance test reults for yourself here and decide what's best for your application.

Integer Arithmetic


Use integers where possible. Because most programs use integers, modern JavaScript engines are optimized for integer operations. In JavaScript all numbers are Number type thus you can't directly specify storage type (int, float, double, etc) like other strongly typed languages. If your application is math heavy, one unintended floating point artimetic can degrade your application performance and it can spread through your application. For example:

function halfVector(v){
	v[0] /= 2;
    v[1] /= 2;
    v[2] /= 2;
}

var v = [3, 5, 9];
halfVector(v);

In a strong typed language like C++ where we used int type we would get [1, 2, 4] as result but in our case we implicitly switched to floating point math in our code.

JavaScript engines use integer math operations where possible and its because modern processors execute integer operations faster than floating point operations. Unlike other objects and floating point values, common integer values are stored in memory where they don't require allocation.

To tell JavaScript engine we want to store integer values in our array in above example we could use bitwise or operator:

function halfIntVector(v){
	v[0] = (v[0] / 2) | 0;
    v[1] = (v[1] / 2) | 0;
    v[2] = (v[2] / 2) | 0;
}

Result of the bitwise or operator is an integer, this way JavaScript engine knows that it should not allocate memory.

Floating Point Values


As stated in previous point, any time a floating point number is assigned to an object property or array element a memory is allocated. If your program does lots of floating point math these allocations can be costly. Although you can't avoid allocation for object properties you can use typed arrays (Float32Array and Float64Array).

Typed arrays can only store floating point values and JavaScript runtime can access and store the values without memory allocations.

Conclusion


There are many other ways to optimize your code but I think this should be enough to get started. Just keep in mind that you should always profile your code and optimize for the portion that takes the most time to execute.

Article Update Log


19 Dec 2013: Updated the article with some tips for integer/float values

12 Dec 2013: Initial release



License


GDOL (Gamedev.net Open License)




Comments

This came just in time! Been playing with JS the past few days for the first time in years, so I'm trying to figure out the best way to handle about basic tasks and such.

Some no-brainers here (if you understand GC), but also some catches. All very helpful in the end. I love how you related it to individual engines and explained what they're doing with your code.

Very good and informative article. I am starting to learn JavaScript for a project with some friends and these sound like good things to keep in mind. Especially since performance is our biggest concern and my code will be running, ideally, dozens of times a second.

There is some good advice here, but I get the feeling that some things could still be explored in more detail. As it stands, the title is a bit misleading, as I was expecting more content. This could become a good article since you seem to know what you are doing.

I would like to see similar article, but more in depth and for C# :)

Could have gone into math libraries a bit further. The things you went into reflect exactly what we had to deal with in a past project. Getting high-performance math code - especially considering that they generally produce or manipulate small arrays - is non-trivial. We had to do things like pre-allocate temporaries in "module" scopes in order to ensure there was no need to ever allocate during gameplay.

Maybe addendum to the good info:

I remember a talk at GDC-EU 2012 where a programmer stated that holding values in int variables wherever possible can save a lot of performance and how hard it was to make sure, as a programmer, that variables stay int and not get converted in memory by your code.

I follow this advice in general and have an extra look while refactoring but unfortunately I never did any measurements, so this may or may not hold up today.

There is some good advice here, but I get the feeling that some things could still be explored in more detail. As it stands, the title is a bit misleading, as I was expecting more content. This could become a good article since you seem to know what you are doing.

Thanks, Please let me know what areas could be improved.

 

 

Could have gone into math libraries a bit further. The things you went into reflect exactly what we had to deal with in a past project. Getting high-performance math code - especially considering that they generally produce or manipulate small arrays - is non-trivial. We had to do things like pre-allocate temporaries in "module" scopes in order to ensure there was no need to ever allocate during gameplay.

Good point, I think this could be an article on its own but I'll try to add more info here

 

 

Maybe addendum to the good info:

I remember a talk at GDC-EU 2012 where a programmer stated that holding values in int variables wherever possible can save a lot of performance and how hard it was to make sure, as a programmer, that variables stay int and not get converted in memory by your code.

I follow this advice in general and have an extra look while refactoring but unfortunately I never did any measurements, so this may or may not hold up today.

Seems this technique is what most compiled Asm.js code use. By using int for storage enables browsers to convert code directly into assembly and it also gets rid of GC. I don't know if its true outside of Asm.js domain but using int is generally faster than strings or objects.

Do you know any resources or examples of profiling/performance testing of JS? What does the procedure look like?

 

I really liked the graph :)

 

 

Seems this technique is what most compiled Asm.js code use. By using int for storage enables browsers to convert code directly into assembly and it also gets rid of GC. I don't know if its true outside of Asm.js domain but using int is generally faster than strings or objects.

This was before the great asm-craze. What struck me was how strongly this topic was stressed.

As said, I never ran into a problem with that, maybe because of sufficient overall coding style, but it was suggested that having some vars accidentally converted to float at runtime grinded his animations on screen to a programmers equivalent of a snail ;-)

Good, +1.

 

Perhaps it would also be helpful to have an article which explains (with examples) how we can profile a Javascript game in (e.g.) Firefox or Chrome. As far as I know, both browsers have some kind of profiler built-in, maybe there are alternatives which are more helpful.

Very interesting article, but it raises some new questions. If I can sum up the GC part of your post, we can say having long-term life variables is better than short-term (and anonymous functions, immutable etc.). It can be a pain for good code design, so I guess it's recommended to optimize only hotspots when necessary.

 

By the way, GC engines can evolve, and they will of course. When looking at the java GC, we can see there are efforts to make short-term variables performing as well as long-term, in order to reduce the need of optimizing against code design. We can hope the same will happen for JS engines.

Great article.

 

I did some test on jsperf.com and I found objects are fast than arrays but typedarrays are faster than objects, at least on Chrome 34

 

http://jsperf.com/raw-array-vs-typedarray

 

http://jsperf.com/raw-object-vs-raw-array-vector

 

(this one array will obviously lose as this.array is still a properties access).

http://jsperf.com/object-vs-array-vector

 

Anyway, you might want to update to point out that you have to use TypedArrays to get the speed?


Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS