Community Pick: Many members of our community have endorsed this article.
Editor's Choice: This article has been selected by our editors as an exceptional contribution.

20 Tips to Optimize your ActionScript

Published:
Introduction

This article is primarily concerned with ActionScript 3 and generally specific to AVM2.  Most suggestions would apply to ActionScript 2 as well, and I've noted those tips that differ between AS2 and AS3.

With the advent of ActionScript 3.0 (hereafter "AS3"), the Flash Player realized significant performance gains.  A few years ago, I ran some performance tests comparing JavaScript to AS2.  ActionScript was noticably slower in every benchmark, usually in the range of 300% or more time required to perform large calculations.  Even animation - Flash's wheelhouse - was slower.  AVM2 and AS3 have changed that, and levelled the playing field to a large degree.

That said, the greatest single contributor to slow performance of Flash applications is poorly written ActionScript.  Below you'll find 20 tips to help you write faster applications.  Please note that the motivation behind each tip is performance, not transparency or best practice.  A performance optimization might directly oppose a best practice, and will very often create opacity in your code.  It's your decision as to what takes priority in your application:  performance, transparency or best practices.

Much of the information presented here will provide only negligible gains as regards performance when used in small-scale code operations.  However, when a developer tweens 3500 raindrops every frame, every line of code needs to be parsed and pared to the sharpest, smallest point possible.

Before I begin, learn to benchmark your code.  There are many approaches, including specialized software or components like frame-rate meters, but the simplest trick I know is to measure the time a loop containing the statements to be tested takes to operate.  After all other setup is performed, on the line immediately before the loop, call the getTimer method and store it in a variable.  Execute your loop (I usually start with 1000 iterations to avoid script timeout, then increase by a factor of 10 until I get a measurable metric).  Immediately following the loop, evaulate the difference by invoking getTimer again and subtracting it from the first.  E.g.,

var start:Number = getTimer();
                      for(var i:int=0;i<1000;i++){
                        // statements to evaluate
                      }
                      trace(start-getTimer());

Open in new window


The Tips

1: Multiple conditionals are faster than stacked operators
When using stacked operators in an evaluation statement, each one is evaluated before returning the result.  Using conditional statements allows the VM to stop checking as soon as it can - when further evaluation is no longer needed.

if(var1) if(var2) if(var3)

Open in new window

is faster than

if(var1 && var2 && var3)

Open in new window



2: Object literals are faster than the new operator
Strings, Numbers (including int and uint), and Boolean objects are usually instantiated using literal notation (var str:String = "text").  Objects and Arrays are often instantiated using the "new" operator - this is seldom necessary (only in the case of a fixed-length Array), and is slower.

var list:Array = [1,2,3];

Open in new window

is faster than

var list:Array = new Array(1,2,3);

Open in new window



3: For loops are faster than while and do-while loops
In AS3, the fastest loop is the for loop (not for.. or for each..in).  While and do-while are slightly slower.  Note that this is a marked contrast to both ActionScript 2 and JavaScript, where while and do-while are about 3 times faster than for loops.  Also, the int datatype is faster than uint or Number (see tip #16).

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

Open in new window

is faster than

var i:int=0;
                      while(i<1000)

Open in new window



4: The in operator is faster than hasOwnProperty
To test if an object has a publically available property, use the "in" operator instead of the hasOwnProperty method.

prop in Object

Open in new window

is faster than

Object.hasOwnProperty("prop")

Open in new window



5: The one-dot rule
When accessing nested variables, anytime a variable requires even one level of scope shift to discover, and is referenced more than once, save it to local variable.  In drawing classes, you'll often see Math.PI / 2 referenced within loops that might iterated hundreds of times each frame - that value should be stored in a local variable.  Note that this is not true when referencing member methods.  Saving Math.PI to a variable is appropriate; saving Math.round to a variable is not.

var var1:Number = container.child.x;
                      textfield.text = var1;
                      container.child2.x = var1;

Open in new window

is faster than

textfield.text = container.child.x;
                      container.child2.x = container.child.x;

Open in new window



6: Dynamic classes are slow - use a public object variable instead
Avoid dynamic classes.  Consider a public variable datatyped to Object instead.

public class MyObject{
                        public var variables:Object = {};
                      }

Open in new window

is faster than

public dynamic class MyObject{
                      }

Open in new window



7: Perform work in enterframes
This one may seem counter-intuitive, and most tutorials and help-boards are peppered with warning against enter frames.  Surprisingly, performing heavy calculation during the enterframe can help "balance the load" and lead to improved performance.


8: Standard string methods are faster than Regular Expressions
Regular Expressions are a great tool and can make complex tasks much easier.  However, standard string methods are almost always faster.

var parts:Array = longString.split("|");

Open in new window

is faster than

var parts:Array = longString.match(/(.*?)\|/gm);

Open in new window



9: Use plain text instead of XML where possible
XML requires the VM to implement a parser, where evaluating plain text requires no special operation.  Where possible, use strings or plain text rather than XML.


10: Work from the top of the stack
When manipulating arrays, work is performed from the top-down.  That means always work from the end of the array, rather than the beginning.  Pop and push are faster than shift and unshift.

MyArray.push(myVar);

Open in new window

is faster than

MyArray.shift(myVar);

Open in new window



11: enterFrames are faster than timers and intervals
Flash handles enterFrames as part of it's native update process - timers and setInterval calls are not.  Actions performed during the enterFrame event perform better than equally frequent Timer events.

addEventListener("enterFrame",myFunc);

Open in new window

is faster than

myTimer.addEventListener("timer",myFunc);

Open in new window



12: Use local variables
When flash detects a variable, it looks first at the local scope - the context of the currently evaluating function.  If not found, it looks at the next "highest" scope, and repeats, until the variable is discovered (or undefined is returned).  Always use local variables when possible.  Note that function arguments are considered local variables.

function getMinutes(){
                        var myVar:uint = 6000;
                        return getTimer() / myVar;
                      }

Open in new window

is faster than

var myVar:uint = 6000;
                      function getMinutes(){
                        return getTimer() / myVar;
                      }

Open in new window



13: Constants are faster than variables
If a variable is unlikely to change, use the const declaration rather than the var declaration.

const URL:String = "http://www.experts-exchange.com";

Open in new window

is faster than

var URL:String = "http://www.experts-exchange.com";

Open in new window



14: Use the as operator instead of casting
Use the "as" operator when possible, instead of casting via constructor.

var child:MyClass = event.currentTarget as MyClass;

Open in new window

is faster than

var child:MyClass = MyClass(event.currentTarget);

Open in new window



15: E4X is extremely slow
E4X is another great tool that can offer drastic savings in development time, but when used on large data sets its performance tends to suffer.  Surprisingly, looping through an XML tree is often much faster than complicated E4X structures.

for each(var node:XML in myXML){
                        if(node.@myProp == "value"){
                          if(node.@anotherProp == 12){
                            react();
                          }
                        }
                      }

Open in new window

is faster than

var nodes:XMLList = myXML.(@myProp=="value").(@anotherProp==12);
                      for each(var node:XML in nodes){
                        react();
                      }

Open in new window



16: int - uint - Number
Of the three Number datatypes, Number is the slowest, int is the fastest, and uint is slightly slower than int.  This applies to simple evaluation only, such as when looping (always set the iterator to the int datatype).  However, performance varies when using more complex evaluation - for example, division operations are performed more quickly on Numbers than ints or uints - I suspect this is because all numbers are cast as Number when performing division, because the return must contain the possibility of a float.

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

Open in new window

is faster than

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

Open in new window



17: Use bitwise methods when possible
Figuring out the math behind bitwise operation can be intimidating, but there are simple ways to use bitwise functionality that can really speed up your code.

var n:Number = 93745298347.230498;
                      var m:Number = n|0;

Open in new window

is faster than

var n:Number = 93745298347.230498;
                      var m:Number = Math.foor(n);

Open in new window



18: Use array.join instead of string concatenation
String concatenation is very slow - use arrays and array methods where possible.

var myArray:Array = [];
                      for(var i:int=0;i<1000;i++){
                        myArray[i] = "text";
                      };
                      var myString:String = myArray.join(" ");

Open in new window

is faster than

var myString:String = "";
                      for(var i:int=0;i<1000;i++){
                        myString += "text ";
                      };

Open in new window



19: Don't evaluate in a loop's conditional test
The condition expression in the for loop is commonly used to evaluate a variable or property - this expression should be a predefined local variable when possible, so that it doesn't need to be evaluated during each iteration of the loop.

var l:uint = myArray.length;
                      for(var i:int=0;i<l;i++)

Open in new window

is faster than

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

Open in new window



20: Use static methods where possible
Static members are created once, and are not copied to instances.

private static function myFunc():void{};

Open in new window

is faster than

private function myFunc():void{};

Open in new window



Conclusion

I hope the tips I've included can help you avoid some less-than-obvious pitfalls on the road to optimization.  I've tried to include information here that isn't found in other AS3 optimization white papers, and tried to keep the superflous information to a minimum.

Additionally, there are optimization techniques found elsewhere that I question.  For example, in AS3, variable name length doesn't matter (it did in AS2).

var a:Number = 987234.230948;

Open in new window

is NOT faster than

var myLongVariableNameUsedToMatterButDoesntAnymore:Number = 987234.230948;

Open in new window


I've also seen developers discourage typing inside a loop, stating that variables that will be referenced within the loop should be typed outside of it.  My own testing does not prove that out.

var n:Number;
                      for(var i:int=0;i<10000;i++){
                        n = Math.random();
                      }

Open in new window

is NOT faster than

for(var i:int=0;i<10000;i++){
                        var n:Number = Math.random();
                      }

Open in new window


Last, a warning: don't try to optimize your code until it's thoroughly debugged.  Make sure everything is working exactly as it should, even under challenging conditions, before worrying about performance.
8
19,144 Views

Comments (11)

Commented:
It's a great idea, with solid ideas that you seem to have researched.

I've got to question #13, though, simply because I've seen an Adobe engineer say the exact opposite:
http://www.senocular.com/flash/tutorials/as3withflashcs3/?page=3#datatyping .  (Scroll down a little bit to the "casting" subsection.)

I'm guessing your suggestion off of your empirical testing?  I've never seen senocular be wrong, but I suppose there could always be a first time.

Aside from that issue - excellent ideas!

Author

Commented:
Hey Carnou,

Thanks for posting - I assume you mean #14 (which is about casting) not #13 (which is about "const" vs "var").  if so, we can easily determine which is faster with a simple test like the once I posted at the beginning of this article:

var bob:Object = new Sprite();
var start:Number = getTimer();
for(var i:int=0;i<500000;i++){
  // var ken:Sprite = Sprite(bob);
  var ken:Sprite = bob as Sprite;
}
trace(getTimer()-start);

casting using the first method comes back about 112 ms on my laptop.
casting using "as" comes back at about 50.

so casting with "as" is more than twice as fast as casting via constructor.

as far as the link you provided, I think he's saying casting with "as" is slower than not casting at all.  if you can avoid casting to begin with, yes that would definitely be preferable, but sometimes it's necessary - often in the case of event.target/currentTarget.  if you're using an extension of Sprite that has a method "myMethod", which is not a member of DisplayObject (the type returned by event.target), you'll need to cast:

function whatever(event:Event):void{
  event.target.myMethod(); // results in failure
  var ref:MySprite = event.target as MySprite;
  ref.myMethod();  // works
}

if you can avoid that situation, and skip casting entirely by virtue of typing, then by all means do so:

var ref:MySprite = new MySprite();
ref.myMethod();  // works, avoids casting, and is preferrable - but not always possible

anyway, thanks again for the vote and your comment
A very enlightening read. thanks

Author

Commented:
Thanks gingermoleman

Commented:
Thanks a lot. Some performance issues like
for(var i:int=0;i<myArray.length;i++) 

Open in new window

gives me impression that this is dumb AS$ :P

View More

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.