<

Still celebrating National IT Professionals Day with 3 months of free Premium Membership. Use Code ITDAY17

x

20 Tips to Optimize your ActionScript

Published on
26,617 Points
16,317 Views
8 Endorsements
Last Modified:
Awarded
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
Comment
Author:moagrius
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
11 Comments
 
LVL 4

Expert Comment

by:Flassari
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.
VM2 uses lazy "and" and "or" conditionals, so they would also stop checking as soon as it can.
You can try it and see:
if (f(true) || f(true)) trace("Statement is true");

function f(returnValue:Boolean ):Boolean {
	trace("f: " + returnValue);
	return returnValue;
}

Open in new window

traces:
f: true
Statement is true

Open in new window

(e is only called once)

And for the AND operator:

if (f(false) && f(true) ) trace("Statement is true");

Open in new window

traces:
f: false

Open in new window


I'm not saying it's faster, just saying it's lazy too =)
0
 
LVL 9

Expert Comment

by:Jakob_E
Looking for more optimization gems - I'll recommend:

http://lab.polygonal.de/2007/05/10/bitwise-gems-fast-integer-math/
0
 
LVL 19

Author Comment

by:moagrius
@Flassari:  very interesting...  i think what's happening is it's executing with a lazy algorithm, but is actually evaluating each one...  notice this:

var start:Number = getTimer();
for(var i:int=0;i<500000;i++){
      if(false && true && true && true && true && true && true && true && true && true){}      
}
trace(getTimer()-start);

runs about 35 ms on my machine, whereas...

var start:Number = getTimer();
for(var i:int=0;i<500000;i++){
      if(false && true) {}      
}
trace(getTimer()-start);

runs about 5 ms, so if it really did quit after the first false, the times should be at least close...

***

and double checking the same number of conditionals versus operators shows it's still quite a bit quicker:

var start:Number = getTimer();
for(var i:int=0;i<500000;i++){
      if(false){
            if(true){
                  if(true){
                        if(true){                  
                        }
                  }
            }
      }      
}
trace(getTimer()-start);

runs about 4 ms, and:

var start:Number = getTimer();
for(var i:int=0;i<500000;i++){
      if(false && true && true && true){}
}
trace(getTimer()-start);

runs about 10 ms, despite the number of conditional checks being equivalent...

anyways, intriguing behavior, thanks for bringing it up!
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 
LVL 19

Author Comment

by:moagrius
@Jakob - those are great - thanks for the link
0
 

Expert Comment

by:Marzieh84
wow ... these tips are really helpful and obviously are the result of years of experience , thank you for sharing :)
0
 
LVL 19

Author Comment

by:moagrius
Thanks Marzieh
0
 
LVL 10

Expert Comment

by:Carnou
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!
0
 
LVL 19

Author Comment

by:moagrius
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
0
 
LVL 5

Expert Comment

by:gingermoleman
A very enlightening read. thanks
0
 
LVL 19

Author Comment

by:moagrius
Thanks gingermoleman
0
 

Expert Comment

by:fg-dev
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
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Join & Write a Comment

The goal of the tutorial is to teach the user how to live broadcast using Flash Media Live Encoder and connecting it to YouTube to broadcast. Log into your Youtube account, choose live stream settings, start live stream from Flash Media Live Enc…
This Micro Tutorial will teach to how to utilize bit rate in Adobe Flash Media Live Encoder.
Other articles by this author

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month