Thursday, March 15, 2007

Array optimizations for AS3/AS2

Geoff sent out a great link for optimizing javascript code and I decided to run some tests with actionscript related to looping over arrays using ideas from the article. These tests produced some interesting results.

I ran several tests doing incremental loops and reversed loops against using "flipped" versions.

Flipped just means this:

var i:Number = array.length - 1;
do {
// something
} while( i--);


The obvious stuff first:

1. AS3 is waaay faster than AS2. Yay, we already knew that ;)
2. All the people that told you to declare the length outside the loop were right.

Now for the interesting stuff:

1. Reversing the loop is faster in all cases.
2. In AS3, using a standard reversed loop is faster than using the flipped version.
3. In AS2, using the flipped reverse version is faster than using the normal reversed version.

Now for something unexpected:

1. Declaring a re-used variable inside the loop was actually faster than declaring it outside the loop. Dunno bout that one, seemed counter intuitive to me. Will explore further.


When to use what:

1. It depends as always on what the inner loop is doing ;)
2. Run tests and implement the method that is faster for your implementation. Cmon, it doesn't take that long to do a couple tests and could dramatically improve performance depending on the situation.

Some helpful utils:

FDT TEMPLATE FOR REVERSE LOOP
var length:Number = ${array}.length - 1;
for (var ${index} : Number = length; ${index} >= 0; ${index}--)
{ ${cursor}
}

FDT TEMPLATE FOR REVERSE FLIPPED LOOP
var ${index}:Number = ${array}.length - 1;
do {
${cursor}
} while ( ${index}-- );

------------------------------------------------

Here's what I did. I setup four methods that basically loop through an array of integers and adds them up. Each method just changed slightly.

1. Normal forward loop without declaring the length outside the loop ( bad )

public function testNormalLoop1(array:Array) :Void {
var startTime:Number = (new Date()).getTime();
var sum:Number = 0;
for( var i:Number = 0; i < array.length; i++ ) {
var value:Number = array[i];
sum+= value;
}
var elapsedTime:Number = (new Date()).getTime() - startTime;
trace("testNormalLoop1() " + elapsedTime);
}


2. Same as #1 but declare length outside the loop
3. Same as #2 but declare var value:Number; outside the loop
4. Same as #3 but get rid of value altogether and just say sum+= array[i];

--------------------------------------------------------------------------

5-8: Same as 1-4 but reverse the loop

--------------------------------------------------------------------------

9-12: Same as 1-4 but use the flipped version. 10-12 calcuate the length outside the loop.

public function testFlippedLoop1(array:Array) :Void {
var startTime:Number = (new Date()).getTime();
var sum:Number = 0;
var i:Number = 0;
do {
i++;
var value:Number = array[i];
sum+= value;
} while ( i < array.length );
var elapsedTime:Number = (new Date()).getTime() - startTime;
trace("testFlippedLoop1() " + elapsedTime);
}

------------------------------------------------------------------------

13-16: Same as 9-12 but reverse the loop.

------------------------------------------------------------------------

17-20: Same as 13-16 but decrement in the while section.


public function testAltFlippedReversedLoop1(array:Array) :Void {
var startTime:Number = (new Date()).getTime();
var sum:Number = 0;
var i:Number = array.length -1;
do {
var value:Number = array[i];
sum+= value;
} while (i-- );
var elapsedTime:Number = (new Date()).getTime() - startTime;
trace("testAltFlippedReversedLoop1() " + elapsedTime);
}


------------------------------------------------------------------------

My Results:

Testing array with length: 1000000
-----------------------------------------------
AS3
----------------------------------------------
testNormalLoop1() 313
testNormalLoop2() 219
testNormalLoop3() 218
testNormalLoop4() 438
testNormalReversedLoop1() 219
testNormalReversedLoop2() 203
testNormalReversedLoop3() 203
testNormalReversedLoop4() 406
testFlippedLoop1() 781
testFlippedLoop2() 563
testFlippedLoop3() 265
testFlippedLoop4() 547
testFlippedReversedLoop1() 609
testFlippedReversedLoop2() 594
testFlippedReversedLoop3() 250
testFlippedReversedLoop4() 562
testAltFlippedReversedLoop1() 547
testAltFlippedReversedLoop2() 547
testAltFlippedReversedLoop3() 219
testAltFlippedReversedLoop4() 453

-----------------------------------------------
AS2
-----------------------------------------------
testNormalLoop1() 1390
testNormalLoop2() 1172
testNormalLoop3() 1188
testNormalLoop4() 1265
testNormalReversedLoop1() 1266
testNormalReversedLoop2() 1250
testNormalReversedLoop3() 1265
testNormalReversedLoop4() 1297
testFlippedLoop1() 1328
testFlippedLoop2() 1109
testFlippedLoop3() 1125
testFlippedLoop4() 1219
testFlippedReversedLoop1() 1359
testFlippedReversedLoop2() 1188
testFlippedReversedLoop3() 1187
testFlippedReversedLoop4() 1266
testAltFlippedReversedLoop1() 1063
testAltFlippedReversedLoop2() 1062
testAltFlippedReversedLoop3() 1063
testAltFlippedReversedLoop4() 1140

No comments: