CPU optimization for Games

August 9th, 2009 by

Hi All,

I got some important facts about memory optimization from my personal experience and various sources . Those are here :

1) Attaching moviclips: for loop vs. while loop
A typical game loop where you attach movieclips to a target mc.
Using the while loop instead of for results in better performance, and if you’re also
reading data from an array then the for in loop should be the best choice.

2) Read data from Array (for, for in, while)
Here we compared the three loops in reading data from an array and the for in is definetely the best way to go.

3) Write data to an array (for, while)
Writing data to an array also shows that the while loop performs better, even if this time the difference is less significant.

4) Single vs Multiple var declaration.
Whenever possible try to assign values to your variables using multiple assignment.
This can speed things up pretty nicely.
If you have in a loop something like this:

a = b = c = 0
d = e = 100

5) Var name lookup
This test shows how important is saving name lookup time especially when insidea looping structure.
Instead of calling Math.random() at every cycle, define a reference to it and use it in the loop, this will save you time because the function name is resolved once, before entering the loop.

Example:

var num = null
t = getTimer()
for (var i=0; i < MAX; i++)
{
        num = Math.floor(MAX) - Math.ceil(MAX)
}
t1.text = "Always lookup: " + (getTimer() - t)

is not as good as:

t = getTimer()
var floor = Math.floor
var ceil  = Math.ceil
for (var i=0; i < MAX; i++)
{
        num = floor(MAX) - ceil(MAX)
}

6) Short vs. Long variable names
Variable names should always be kept as short as possible.
In this test you can see how long named variables can negatively affect speed.
We suggest to use very short names (1 or 2 chars) when optimizing performance critical part of your code.

7) Declaring var in loops vs. declaring before loops
Before running this benchmark we thought that declaring a local var before a loop
structure could save some xtra time, but tests proved the exact contrary.

// Inside
t = getTimer()
for (var i=0; i < MAX; i++)
{
        var test1 = i
}
t1.text = "Inside:" + (getTimer() - t)
// Outside
t = getTimer()
var test2
for (var i=0; i < MAX; i++)
{
        test2 = i
}

8) Using nested if
Whenever using complex conditional expressions you can optimize the code by breaking them into single nested conditions.
This way you don’t have to evaluate all the expressions all the times.
The following code is what we used for the benchmark: even if the conditional expressions are pretty simple the speed increse is significant.
Also you can notice that the expression that evaluates as false is in 3rd position, so you may even get better performance when it is placed in 2nd or 1st position.

MAX = 20000
a = 1
b = 2
c = -3
d = 4
var i=MAX
while(--i > -1)
{
        if (a == 1 && b == 2 && c == 3 && d == 4)
        {
                 var k = d * c * b * a
        }
}
var i=MAX
while(--i > -1)
{
        if (a == 1)
        {
                 if (b == 2)
                 {
                          if (c == 3)
                          {
                                   if (d == 4)
                                   {
                                           var k = d * c * b * a
                                   }
                          }
                 }
        }
}

9) Nested loops (for vs. while)
Nested loops are a common programming technique to access 2 dimensional data, like map arrays in a tile based game, etc…
As you may expect from the previous tests the while loop performs better.

10). Accessing local vars (This vs. With vs. Reference)
Local variables and objects can be referenced in more than one way.
We’ve tested two ways to do it and found out that the usage of the keyword this produces the worst results.
More speed can be achieved using with operator.
The following code was used as a test, running a 10000 iteration loop

obj = {}
obj.a = 1
obj.b = 2
obj.c = 3
obj.d = 4
obj.e = 5
obj.f = 6
obj.g = 7
obj.h = 8
obj.test1 = useThis
obj.test2 = useWith
MAX = 10000
function useThis()
{
        var i = MAX
        while(--i > -1)
        {
                 this.a = 1
                 this.b = 2
                 this.c = 3
                 this.d = 4
                 this.e = 5
                 this.f = 6
                 this.g = 7
                 this.h = 8
        }
}
function useWith()
{
        var i = MAX
        while(--i > -1)
        {
                 with(this)
                 {
                          a = 1
                          b = 2
                          c = 3
                          d = 4
                          e = 5
                          f = 6
                          g = 7
                          h = 8
                 }
        }
}

Array indexing

Let’s start with a simple loop through the items of an array:

private function method1() : void

{

var tmpVar:int;

for(var i:Number=0; i<testArray.length; i++)

{

tmpVar = testArray[i];

}

}

Looping through this method 50 times gives an average time of 53.34 milliseconds. Now let’s change the type of iterator variable i from Number to int:

for(var i:int=0; i<testArray.length; i++)

This results in an average time of 35.58 milliseconds. This is because array indexing is faster with ints. We can also speed up the loop by storing the array’s length in a variable outside of the loop:

var l:int = testArray.length;

for(var i:int=0; i<l; i++)

Time taken: 21.6 milliseconds!

Constants from other classes

Another optimization that I found is in the way you use constants from another class. Take for instance this piece of code:

var tmpVar:int;

for(var i:Number=0; i<100000; i++)

{

tmpVar = SomeClass.SOME_CONSTANT;

}

This takes 34.08 milliseconds to execute. If we move the constant lookup outside of the iteration, like this:

var tmpVar:int;

var myConstant:int = SomeClass.SOME_CONSTANT;

for(var i:Number=0; i<100000; i++)

{

tmpVar = myConstant;

}

Now it only takes 15.8 milliseconds

Variable instantiation

Variable instantation in this fashion:

for(var i:int=0; i<100000; i++)

{

var v1:Number=10;

var v2:Number=10;

var v3:Number=10;

var v4:Number=10;

var v5:Number=10;

}

takes 46.52 millis. This can be speeded up if you use the “var” keyword one time and then declare the variables on a single line, like this:

for(var i:int=0; i<100000; i++)

{

var v1:Number=10, v2:Number=10, v3:Number=10, v4:Number=10, v5:Number=10;

}

which results in an average time of 19.74 milliseconds.

Bitwise operators

Bitwise operators can also increase performance. Take for instance multiplication and division:

for(var i:int=0; i<100000; i++)

{

var val1:int = 4 * 2;

var val2:int = 4 * 4;

var val3:int = 4 / 2;

var val4:int = 4 / 4;

}

Average time: 49.12 ms, using bitwise operators it is taken down to 35.56 ms:

for(var i:int=0; i<100000; i++)

{

var val1:int = 4 << 1;

var val2:int = 4 << 2;

var val3:int = 4 >> 1;

var val4:int = 4 >> 2;

}

Unnecessary import statements

Not the biggest time saver unless you are generating many instances of the class or the class has a large number of function calls made to it. However, it is a very easy way to improve performance.

Sprites and MovieClips

You only need to use the MovieClip type if the object has multiple frames, you can export for ActionScript Library objects extending the Sprite class instead of the MovieClip class.

Avoid using masks

If you need to mask a square area use the display object’s ’scrollRect’ property instead. If you need a shape that is not a square, place that shape over the object with the scrollRect property.

Disable events that you do not need

Dispatching events is one of the more expensive function calls that you can make. Every class that extends InteractiveObject (which encompasses every display object) generates a host of events that you most likely do not need. Take the time to set MouseEnabled and MouseChildren to false to keep your event system as clean as possible. Also, make sure to remove event listeners and onEnterFrame events when you no longer need them.

Limit your art assets attached to the stage

No sense in traversing render nodes if they are not actively being used. If it is an object that is not frequently visible I will completely destroy it and recreate it when it is needed again. The archetype to employ this strategy is menu systems

Resource management strategies in Flash Player 9

  • A game sprite subscribes to its own enterFrame event. In every frame it moves, the application performs some calculations to determine its proximity to other game elements. In ActionScript 3.0, even after you remove the sprite from the display list and null all references to it, the application continues to run that code in every frame until it is removed by garbage collection. You must remember to explicitly remove the enterFrame listener when the sprite is removed.
  • Consider a movie clip that follows the mouse by subscribing to the Stage’s mouseMove event—which is the only way to achieve this effect in the new event model. Unless you remember to remove the listener, the clip will continue to execute code every time the mouse is moved, even after the clip is “deleted.” By default, the clip executes forever because a reference to it exists from the Stage for event dispatch. I will discuss how to avoid this issue in a future article.

import flash.system.System;

import flash.net.navigateToURL;

import flash.net.URLRequest;

// check our memory every 1 second:

var checkMemoryIntervalID:uint = setInterval(checkMemoryUsage,1000);

var showWarning:Boolean = true;

var warningMemory:uint = 1000*1000*500;

var abortMemory:uint = 1000*1000*625;

function checkMemoryUsage() {

if (System.totalMemory > warningMemory && showWarning) {

// show an error to the user warning them that we’re running out of memory and might quit

// try to free up memory if possible

showWarning = false; // so we don’t show an error every second

} else if (System.totalMemory > abortMemory) {

// save current user data to an LSO for recovery later?

abort();

}

}

function abort() {

// send the user to a page explaining what happpened:

navigateToURL(new URLRequest(“memoryError.html”));

}

Array & Object constructing

new Array() and new Object() are about 3 times slower than [] and {}.

Index Number type for Arrays

list[int(0)] is now faster than list[0]!

Nulling Array vs. Splicing Array

When working with large Arrays splicing is obviously an expensive operation, you can avoid this by nulling the index and skipping it in a null scenario. If you need to splice in order to keep the Array length low. Then store these nulled indexes in another trash Array once the garbage count has reached a limit you’ve defined loop through the numerically sorted trash indexes deleting splices in bulk. This concept is demonstrated in Tweensy.

Array push vs. Array index

Don’t use the push function on an Array instead use the Array length property and set on the Array like so list[list.length] = data; Or if that’s not enough for the best performance (about 600% faster) than using push don’t use the Array length property at all. To do this keep the Array length as an external variable and increment/decrement it as you add and remove items from the Array.

Array emptying – length 0 vs. A new Array

If you need to empty an Array there is a handy option to do this via setting the Arrays property length to 0. You would think this would be the better option as you are avoiding creating a new Array and ultimately saving memory. Though it’s interesting to know as a trade off using length to clear an Array is not faster in performance (in most cases). Using length is more efficient when you might need to clear more than 510 Arrays in a single execution, otherwise creating a new Array via [] is the faster option again.

Var declarations on multiple lines vs. Var declarations on a single line

If you need to declare a few variables it’s slightly more efficient to do so on a single line i.e.
var a:int=0, b:int=0, c:int=0;
vs.
var a:int=0;
var b:int=0;
var c:int=0;

Using Xor to swap variables

Using Xor is very handy if you want to swap variables but don’t want to create a third variable i.e.

a = a^b;
b = a^b;
a = a^b;
Like most people I thought this would be more efficient but it’s not compared to creating a third variable and then doing the swap. In fact it’s about 300% more efficient to do the swap this way. Though honestly I still have a soft spot for the Xor method.

var oldB:int = b;
b = a;
a = oldB;

Multiplication vs. Division

When working with Math in Actionscript multiplications seem to always run faster than divisions so instead of 5000/1000 use 5000*0.001 it’s about 130% faster.

Type casting comparison

When type casting the keyword as is 250% more efficient than casting by Type(item); Though surprisingly not using either is about 1400% more efficient.

Long vs Short variable names

Though this one is debatable because it results in illegible code. Shortened variable names do give a millisecond or so improvement if iterating many 1,000’s of times in a single loop. Though I think this is a last resort in code optimization as the differences are really marginal.


Tags: , , , , , , , , ,