# ActionScript 3.0 Optimization: A Practical Example

##### Tutorial Details
• Difficulty: Intermediate
• Platform: Flash (Flash Player 10.1)
• Language: AS3
• Software used: Flash Professional CS4
• Estimated Completion Time: 30 minutes

Code optimization aims to maximize the performance of your Flash assets, while using as little of the system’s resources – RAM and CPU – as possible. In this tutorial, starting off with a working but resource-hogging Flash app, we will gradually apply many optimization tweaks to its source code, finally ending up with a faster, leaner SWF.

## Final Result Preview

Let’s take a look at the final result we will be working towards:

Note that the “Memory Used” and “CPU Load” stats are based on all the SWFs you have open across all browser windows, including Flash banner ads and the like. This may make the SWF appear more resource intensive than it actually is.

## Step 1: Understanding the Flash Movie

The Flash movie has two main elements: a particle simulation of fire, and a graph showing the animation’s resource consumption over time. The graph’s pink line tracks the total memory consumed by the movie in megabytes, and the green line plots CPU load as a percentage.

ActionScript objects take up most of the memory allocated to the Flash Player, and the more ActionScript objects a movie contains, the higher its memory consumption. In order to keep a program’s memory consumption low, the Flash Player regularly does some garbage collection by sweeping through all ActionScript objects and releasing from memory those no longer in use.

A memory consumption graph normally reveals a hilly up-down pattern, dipping each time garbage collection is performed, then slowly rising as new objects are created. A graph line that’s only going up points to a problem with garbage collection, as it means new objects are being added to memory, while none are being removed. If such a trend continues, the Flash player may eventually crash as it runs out of memory.

The CPU load is calculated by tracking the movie’s frame rate. A Flash movie’s frame rate is much like its heartbeat. With each beat, the Flash Player updates and renders all on-screen elements and also runs any required ActionScript tasks.

It is the frame rate that determines how much time Flash Player should spend on each beat, so a frame rate of 10 frames per second (fps) means at least 100 milliseconds per beat. If all the required tasks are performed within that time, then Flash Player will wait for the remaining time to pass before moving on to the next beat. On the other hand, if the required tasks in a particular beat are too CPU intensive to be completed within the given time frame, then the frame rate automatically slows down to allow for some extra time. Once the load lightens, the frame rate speeds up again, back to the set rate.

(The frame rate may also be automatically throttled down to 4fps by the Flash Player when the program’s parent window looses focus or goes offscreen. This is done to conserve system resources whenever the user’s attention is focused elsewhere.)

What this all means is that there are actually two kinds of frame rates: the one you originally set and hope your movie always runs at, and the one it actually runs at. We’ll call the one set by you the target frame rate, and the one it actually runs at the actual frame rate.

The graph’s CPU load is calculated as a ratio of actual to target frame rate. The formula used to calculate this is:

CPU load = ( target frame rate - actual frame rate ) / actual frame rate * 100

For example, if the target frame rate is set to 50fps but the movie actually runs at 25fps, the CPU load will be 50% – that is, ( 50 - 25 )/ 50 * 100.

Please note that this is not the actual percentage of system CPU resources used by the running movie, but rather a rough estimate of the actual value. For the optimization process outlined here, this estimate is a good enough metric for the task at hand. To get the actual CPU usage, use the tools provided by your operating system, e.g. the Task Manager in Windows. Looking at mine it right now, it shows the unoptimized movie is using 53% of CPU resources, while the movie’s graph shows a CPU load of 41.7%.

PLEASE NOTE: All the movie screenshots in this tutorial were taken from the standalone version of Flash Player. The graph will most likely show different numbers on your system, depending on your operating system, browser, and Flash Player version. Having any other currently running Flash apps in different browser windows or flash players may also affect the memory use reported by some systems. When analyzing the perfomance of your program, always ensure that no other Flash programs are running as they may corrupt your metrics.

With the CPU load, expect it to shoot up to over 90% whenever the movie goes off screen – for example if you switch to another browser tab or scroll down the page. The lower frame rate that causes this will not be caused by CPU intensive tasks, but by Flash throttling down the frame rate whenever you look elsewhere. Whenever this happens, wait a few seconds for the CPU load graph to settle to its proper CPU load values after the normal frame rate kicks in.

## Step 2: Does This Code Make My Flash Look Fat?

The movie’s source code is shown below and contains just one class, named Flames, which is also the document class. The class contains a set of properties to keep track of the movie’s memory and CPU load history, which is then used to draw a graph. The memory and CPU load statistics are calculated and updated in the  Flames.getStats()  method, and the graph is drawn by calling  Flames.drawGraph()  on each frame. To create the fire effect, the  Flames.createParticles()  method first generates hundreds of particles each second, which are stored in the  fireParticles array. This array is then looped through by  Flames.drawParticles() , which uses each particle’s properties to create the effect.

Take some time to study the  Flames  class. Can you already spot any quick changes that will go a long way in optimizing the program?



package com.pjtops{
import flash.display.MovieClip;
import flash.events.Event;
import fl.motion.Color;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.system.System;
import flash.utils.getTimer;

public class Flames extends MovieClip{

private var memoryLog = new Array(); // stores System.totalMemory values for display in the graph
private var memoryMax = 0; // the highest value of System.totalMemory recorded so far
private var memoryMin = 0;	// the lowest value of System.totalMemory recorded so far
private var memoryColor; // the color used by text displaying memory info

private var ticks = 0; // counts the number of times getStats() is called before the next frame rate value is set
private var frameRate = 0;	//the original frame rate value as set in Adobe Flash
private var cpuLog = new Array(); // stores cpu values for display in the graph
private var cpuMax = 0; // the highest cpu value recorded so far
private var cpuMin = 0; // the lowest cpu value recorded so far
private var cpuColor;	// the color used by text displaying cpu
private var cpu; // the current calculated cpu use

private var lastUpdate = 0; // the last time the framerate was calculated
private var sampleSize = 30; // the length of memoryLog & cpuLog
private var graphHeight;
private var graphWidth;

private var fireParticles = new Array(); // stores all active flame particles
private var fireMC = new MovieClip(); // the canvas for drawing the flames
private var palette = new Array(); // stores all available colors for the flame particles
private var anchors = new Array(); // stores horizontal points along fireMC which act like magnets to the particles
private var frame; // the movieclips bounding box

// class constructor. Set up all the events, timers and objects
public function Flames() {
frame = new Rectangle( 2, 2, stage.stageWidth - 2, stage.stageHeight - 2 );

var colWidth = Math.floor( frame.width / 10 );
for( var i = 0; i < 10; i++ ){
anchors[i] = Math.floor( i * colWidth );
}

setPalette();

memoryColor = memoryTF.textColor;
cpuColor = cpuTF.textColor;
graphHeight = graphMC.height;
graphWidth = graphMC.width;

frameRate = stage.frameRate;

}

//creates a collection of colors for the flame particles, and stores them in palette
private function setPalette(){
var black = 0x000000;
var blue = 0x0000FF;
var red = 0xFF0000;
var orange = 0xFF7F00;
var yellow = 0xFFFF00;
var white = 0xFFFFFF;
palette = palette.concat( getColorRange( black, blue, 10 ) );
palette = palette.concat( getColorRange( blue, red, 30 ) );
palette = palette.concat( getColorRange( red, orange, 20 ) );
palette = palette.concat( getColorRange( orange, yellow, 20 ) );
palette = palette.concat( getColorRange( yellow, white, 20 ) );
}

//returns a collection of colors, made from different mixes of color1 and color2
private function getColorRange( color1, color2, steps){
var output = new Array();
for( var i = 0; i < steps; i++ ){
var progress = i / steps;
var color = Color.interpolateColor( color1, color2, progress );
output.push( color );
}
return output;
}

// calculates statistics for the current state of the application, in terms of memory used and the cpu %
private function getStats( event ){
ticks++;
var now = getTimer();

if( now - lastUpdate < 1000 ){
return;
}else {
lastUpdate = now;
}

cpu = 100 - ticks / frameRate * 100;
cpuLog.push( cpu );
ticks = 0;
cpuTF.text = cpu.toFixed(1) + '%';
if( cpu > cpuMax ){
cpuMax = cpu;
cpuMaxTF.text = cpuTF.text;
}
if( cpu < cpuMin || cpuMin == 0 ){
cpuMin = cpu;
cpuMinTF.text = cpuTF.text;
}

var memory = System.totalMemory / 1000000;
memoryLog.push( memory );
memoryTF.text = String( memory.toFixed(1) ) + 'mb';
if( memory > memoryMax ){
memoryMax = memory;
memoryMaxTF.text = memoryTF.text;
}
if( memory < memoryMin || memoryMin == 0 ){
memoryMin = memory;
memoryMinTF.text = memoryTF.text;
}
}

//render's a graph on screen, that shows trends in the applications frame rate and memory consumption
private function drawGraph( event ){
graphMC.graphics.clear();
var ypoint, xpoint;
var logSize = memoryLog.length;

if( logSize > sampleSize ){
memoryLog.shift();
cpuLog.shift();
logSize = sampleSize;
}
var widthRatio = graphMC.width / logSize;

graphMC.graphics.lineStyle( 3, memoryColor, 0.9 );
var memoryRange = memoryMax - memoryMin;
for( var i = 0; i < memoryLog.length; i++ ){
ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
xpoint = (i / sampleSize) * graphWidth;
if( i == 0 ){
graphMC.graphics.moveTo(  xpoint, -ypoint );
continue;
}
graphMC.graphics.lineTo( xpoint, -ypoint );
}

graphMC.graphics.lineStyle( 3, cpuColor, 0.9 );
for( var j = 0; j < cpuLog.length; j++ ){
ypoint = cpuLog[j] / 100 * graphHeight;
xpoint = ( j / sampleSize ) * graphWidth;
if( j == 0 ){
graphMC.graphics.moveTo(  xpoint, -ypoint );
continue;
}
graphMC.graphics.lineTo( xpoint, -ypoint );
}
}

//renders each flame particle and updates it's values
private function drawParticles( event ) {
createParticles( 20 );

fireMC.graphics.clear();
for ( var i in fireParticles ) {
var particle = fireParticles[i];

if (particle.life == 0 ) {
delete( fireParticles[i] );
continue;
}

var size = Math.floor( particle.size * particle.life/100 );
var color = palette[ particle.life ];
var transperency = 0.3;

if( size < 3 ){
size *= 3;
color = 0x333333;
particle.x += Math.random() * 8 - 4;
particle.y -= 2;
transperency = 0.2;
}else {
particle.y = frame.bottom - ( 100 - particle.life );

if( particle.life > 90 ){
size *= 1.5;
}else if( particle.life > 45){
particle.x += Math.floor( Math.random() * 6 - 3 );
size *= 1.2;
}else {
transperency = 0.1;
size *= 0.3;
particle.x += Math.floor( Math.random() * 4 - 2 );
}
}

fireMC.graphics.lineStyle( 5, color, 0.1 );
fireMC.graphics.beginFill( color, transperency );
fireMC.graphics.drawCircle( particle.x, particle.y, size );
fireMC.graphics.endFill();
particle.life--;
}
}

//generates flame particle objects
private function createParticles( count ){
var anchorPoint = 0;
for(var i = 0; i < count; i++){
var particle = new Object();
particle.x = Math.floor( Math.random() * frame.width / 10 ) + anchors[anchorPoint];
particle.y = frame.bottom;
particle.life = 70 + Math.floor( Math.random() * 30 );
particle.size = 5 + Math.floor( Math.random() * 10 );
fireParticles.push( particle );

if(particle.size > 12){
particle.size = 10;
}
particle.anchor = anchors[anchorPoint] + Math.floor( Math.random() * 5 );

anchorPoint = (anchorPoint == 9)? 0 : anchorPoint + 1;
}
}

}
}


It’s a lot to take in, so don’t worry – we’ll go through the various improvements in the rest of this tutorial.

## Step 3: Use Strong Typing by Assigning Data Types to All Variables

The first change we’ll make to the class is to specify the data type of all declared variables, method parameters, and method return values.

For example, changing this

protected var memoryLog = new Array();
protected var memoryMax = 0; // yes, but what exactly are you?


to this.

protected var memoryLog:Array = new Array();
protected var memoryMax:Number = 0; // memoryMax the Number, much better!


Whenever declaring variables, always specify the data type, as this allows the Flash compiler to perform some extra optimizations when generating the SWF file. This alone can lead to big performance improvements, as we’ll soon see with our example. Another added benefit of strong typing is that the compiler will catch and alert you of any data-type related bugs.

## Step 4: Examine Results

This screen shot shows the new Flash movie, after applying strong typing. We can see that while it’s had no effect on the current or maximum CPU load, the minimum value has dropped from 8.3% to 4.2%. The maximum memory consumed has gone down from 9MB to 8.7MB.

The slope of the graph’s memory line has also changed, compared to the one shown in Step 2. It still has the same jagged pattern, but now drops and rises at a slower rate. This is a good thing, if you consider that the sudden drops in memory consumption are caused by Flash Player’s garbage collection, which is usually triggered when allocated memory is about to run out. This garbage collection can be an expensive operation, since Flash Player has to traverse through all the objects, looking for those that are no longer needed but still taking up memory. The less often it has to do this, the better.

## Step 5: Efficiently Store Numeric Data

Actionscript provides three numeric data types:  Number  ,  uint  and  int . Of the three types, Number consumes the most memory as it can store larger numeric values than the other two. It is also the only type able to store numbers with decimal fractions.

The  Flames  class has many numeric properties, all of which use the Number data type. As  int  and  uint  are more compact data types, we can save some memory by using them instead of Numbers in all situations where we don’t need decimal fractions.

A good example is in loops and Array indexes, so for example we are going to change

for( var i:Number = 0; i < 10; i++ ){
anchors[i] = Math.floor( i * colWidth );
}


into

for( var i:int = 0; i < 10; i++ ){
anchors[i] = Math.floor( i * colWidth );
}


The properties  cpu ,  cpuMax  and  memoryMax  will remain Numbers, as they will most likely store fractional data, while  memoryColor ,  cpuColor and  ticks can be changed to uints, as they will always store positive, whole numbers.

## Step 6: Minimize Method Calls

Method calls are expensive, especially calling a method from a different class. It gets worse if that class belongs to a different package, or is a static method. The best example here is the  Math.floor()  method, used throughout the  Flames  class to roundoff fractional numbers. This method call can be avoided by using uints instead of Numbers to store whole numbers.

// So instead of having:
anchors[i] = Math.floor( i * colWidth );
// we instead cast the value to a uint
anchors[i] = uint( i * colWidth );

// The same optimization can be performed by simply assigning the uint data type, for example changing
var size:uint = Math.floor( particle.size * particle.life/100 );
// into
var size:uint = particle.size * particle.life/100;


In the example above, the call to  Math.floor()  is unnecessary, since Flash will automatically round off any fractional number value assigned to a uint.

## Step 7: Multiplication Is Faster Than Division

Flash Player apparently finds multiplication easier than division, so we’ll go through the  Flames  class and convert any division math into the equivalent multiplication math. The conversion formula involves getting the reciprocal of the number on the right side of the operation, and multiplying it with the number on the left. The reciprocal of a number is calculated by dividing 1 by that number.

var colWidth:uint = frame.width / 10; //division by ten
var colWidth:uint = frame.width * 0.1; //produces the same result as multiplication by 0.1


Lets take a quick look at the results of our recent optimization efforts. The CPU load has finally improved by dropping from 41.7% to 37.5%, but the memory consumption tells a different story. Maximum memory has risen to 9.4MB, the highest level yet, and the graph’s sharp, saw-tooth edges shows that garbage collection is being run more often again. Some optimization techniques will have this inverse effect on memory and CPU load, improving one at the expense of the other. With memory consumption almost back to square one, a lot more work still needs to be done.

## Step 8: Recycling Is Good for the Environment

You too can play your part in saving the environment. Recycle your objects when writing your AS3 code reduce the amount of energy consumed by your programs. Both the creation and destruction of new objects are expensive operations. If your program is constantly creating and destroying objects of the same type, big performance gains can be achieved by recycling those objects instead. Looking at the  Flames  class, we can see that a lot of particle objects are being created and destroyed every second:

private function drawParticles( event ):void {
createParticles( 20 );

fireMC.graphics.clear();
for ( var i:* in fireParticles ) {
var particle:Object = fireParticles[i];

if (particle.life == 0 ) {
delete( fireParticles[i] );
continue;
}


There are many ways to recycle objects, most involve creating a second variable to store unneeded objects instead of deleting them. Then when a new object of the same type is required, it is retrieved from the store instead of creating a new one. New objects are only created when the store is empty. We are going to do something similar with the particle objects of the  Flames  class.

First, we create a new array called  inactiveFireParticles[] , which stores references to particles whose life property is zero (dead particles). In the  drawParticles()  method, instead of deleting a dead particle, we add it to the  inactiveFireParticles[]  array.

private function drawParticles( event ):void {
createParticles( 20 );

fireMC.graphics.clear();
for ( var i:* in fireParticles ) {
var particle:Object = fireParticles[i];

if( particle.life <= 0 ) {
if( particle.life == 0 ){
particle.life = -1;
inactiveFireParticles.push( particle );
}
continue;
}


Next, we modify the  createParticles()  method to first check for any stored particles in the  inactiveFireParticles[]  array, and use them all before creating any new particles.

private function createParticles( count ):void{
var anchorPoint = 0;
for(var i:uint = 0; i < count; i++){

var particle:Object;
if( inactiveFireParticles.length > 0 ){
particle = inactiveFireParticles.shift();
}else {
particle = new Object();
fireParticles.push( particle );
}

particle.x = uint( Math.random() * frame.width * 0.1 ) + anchors[anchorPoint];
particle.y = frame.bottom;
particle.life = 70 + uint( Math.random() * 30 );
particle.size = 5 + uint( Math.random() * 10 );

if(particle.size > 12){
particle.size = 10;
}
particle.anchor = anchors[anchorPoint] + uint( Math.random() * 5 );

anchorPoint = (anchorPoint == 9)? 0 : anchorPoint + 1;
}
}


## Step 9: Use Object and Array Literals Whenever Possible

When creating new objects or arrays, using the literal syntax is faster than using the new operator.

private var memoryLog:Array = new Array(); // array created using the new operator
private var memoryLog:Array = []; // array created using the faster array literal

particle = new Object(); // object created using the new operator
particle = {}; // object created using the faster object literal


## Step 10: Avoid Using Dynamic Classes

Classes in ActionScript can either be sealed or dynamic. They’re sealed by default, meaning the only properties and methods an object derived from it can have must have been defined in the class. With dynamic classes, new properties and methods can be added at runtime. Sealed classes are more efficient than dynamic classes, because some Flash Player performance optimizations can be done when all the possible functionality that a class can ever have are known beforehand.

Within the  Flames  class, the thousands of particles extend the built-in Object class, which is dynamic. Since no new properties need to be added to a particle at runtime, we’ll save up more resources by creating a custom sealed class for the particles.

Here is the new Particle, which has been added to the same Flames.as file.

class Particle{
public var x:uint;
public var y:uint;
public var life:int;
public var size:Number;
public var anchor:uint;
}


The  createParticles () method will also be adjusted, changing the line

var particle:Object;
particle = {};


var particle:Particle;
particle = new Particle();


## Step 11: Use Sprites When You Don’t Need the Timeline

Like the Object class, MovieClips are dynamic classes. The MovieClip class inherits from the Sprite class, and the main difference between the two is that MovieClip has a timeline. Since Sprites have all the functionality of MovieClips minus the timeline, use them whenever you need a DisplayObject that does not need the timeline. The  Flames  class extends the MovieClip but it does not use the timeline, as all its animation is controlled through ActionScript. The fire particles are drawn on  fireMC , which is also a MovieClip that does not make use of its timeline.

We change both  Flames  and  fireMC  to extend Sprite instead, replacing:

import flash.display.MovieClip;
private var fireMC:MovieClip = new MovieClip();
public class Flames extends MovieClip{


with

import flash.display.Sprite;
private var fireMC:Sprite = new Sprite();
public class Flames extends Sprite{


## Step 12: Use Shapes Instead of Sprites When You Don’t Need Child Display Objects or Mouse Input

The Shape class is even lighter than the Sprite class, but it cannot support mouse events or contain child display objects. As the  fireMC  requires none of this functionality, we can safely turn it into a Shape.

import flash.display.Shape;
private var fireMC:Shape = new Shape();


The graph shows big improvements in memory consumption, with it dropping and remaining stable at 4.8MB. The saw-tooth edges have been replaced by an almost straight horizontal line, meaning garbage collection is now rarely run. But the CPU load has mostly gone back again to its original high level of 41.7%.

## Step 13: Avoid Complex Calculations Inside Loops

They say over 50% of a program’s time is spent running 10% of its code, and most of that 10% is most likely to be taken up by loops. Many loop optimization techniques involve placing as much of the CPU intensive operations outside the body of a loop. These operations include object creation, variable lookups and calculations.

for( var i = 0; i < memoryLog.length; i++ ){
// loop body
}


The first loop in the  drawGraph()  method is shown above. The loop runs through every item of the  memoryLog  array, using each value to plot points on the graph. At the start of each run, it looks up the length of the  memoryLog  array and compares it with the loop counter. If the  memoryLog  array has 200 items, the loop runs 200 times, and performs this same lookup 200 times. Since the length of  memoryLog  does not change, the repeated lookups are wasteful and unnecessary. It’s better to look up the value of  memoryLog.length  just once before the lookup begins and store it in a local variable, since accessing a local variable will be faster than accessing an object’s property.

var memoryLogLength:uint = memoryLog.length;
for( var i = 0; i < memoryLogLength; i++ ){
// loop body
}


In the  Flames  class, we adjust the two loops in the  drawGraph()  method as shown above.

## Step 14: Place Conditional Statements Most Likely to Be True First

Consider the block of  if..else  conditionals below, derived from the  drawParticles () method:

if( particle.life > 90 ){ // a range of 10 values, between 91 - 100
size *= 1.5;
}else if( particle.life > 45){ // a range of 45 values, between 46 - 90
particle.x += Math.random() * 6 - 3;
size *= 1.2;
}else {	// a range of 45 values, values between 0 - 45
transperency = 0.1;
size *= 0.3;
particle.x += Math.random() * 4 - 2;
}


A particle’s life value can be any number between 0 and 100. The  if  clause tests whether the current particle’s life is between 91 to 100, and if so it executes the code within that block. The  else-if  clause tests for a value between 46 and 90, while the  else  clause takes the remaining values, those between 0 and 45. Considering the first check is also the least likely to succeed as it has the smallest range of numbers, it should be the last condition tested. The block is rewritten as shown below, so that the most likely conditions are evaluated first, making the evaluations more efficient.

if( particle.life < 46 ){
transperency = 0.1;
size *= 0.3;
particle.x += Math.random() * 4 - 2;
}else if( particle.life < 91 ){
particle.x += Math.random() * 6 - 3;
size *= 1.2;
}else {
size *= 1.5;
}


## Step 15: Add Elements to the End of an Array Without Pushing

The method  Array.push()  is used quite a lot in the  Flames  class. It will be replaced by a faster technique that uses the array’s length property.

cpuLog.push( cpu ); // slow and pretty
cpuLog[ cpuLog.length ] = cpu; // fast and ugly


When we know the length of the array, we can replace  Array.push()  with an even faster technique, as shown below.

var output:Array = [];	//output is a new, empty array. Its length is 0
for( var i:uint = 0; i < steps; i++ ){ // the value of i also starts at zero. Each loop cycle increases both i and output.length by one
var progress:Number = i / steps;
var color:uint = Color.interpolateColor( color1, color2, progress );
output[i] = color;	// faster than cpuLog[ cpuLog.length ] = cpu;
}


## Step 16: Replace Arrays With Vectors

The Array and Vector classes are very similar, except for two major differences: Vectors can only store objects of the same type, and they’re more efficient and faster than arrays. Since all the arrays in the  Flames  class either store variables of only one type – ints, uints or Particles, as required – we shall convert them all to Vectors.

These arrays:

private var memoryLog:Array = [];
private var cpuLog:Array = [];
private var fireParticles:Array = [];
private var palette:Array = [];
private var anchors:Array = [];
private var inactiveFireParticles:Array = [];


…are replaced with their Vector equivalents:

private var memoryLog:Vector.<Number> = new Vector.<Number>();
private var cpuLog:Vector.<Number> = new Vector.<Number>();
private var fireParticles:Vector.<Particle> = new Vector.<Particle>();
private var palette:Vector.<uint> = new Vector.<uint>();
private var anchors:Vector.<uint> = new Vector.<uint>();
private var inactiveFireParticles:Vector.<Particle> = new Vector.<Particle>();


Then we modify the  getColorRange() method to work with Vectors rather than arrays.

private function getColorRange( color1, color2, steps):Vector.<uint>{
var output:Vector.<uint> =  new Vector.<uint>();
for( var i:uint = 0; i < steps; i++ ){
var progress:Number = i / steps;
var color:uint = Color.interpolateColor( color1, color2, progress );
output[i] = color;
}
return output;
}


## Step 17: Use the Event Model Sparingly

While very convenient and handy, the AS3 Event Model is built on top of an elaborate setup of event listeners, dispatchers and objects; then there is event propagation and bubbling and much more, all of which a book can be written about. Whenever possible, always call a method directly rather than through the event model.

addEventListener( Event.ENTER_FRAME, drawParticles );


The  Flames  class has three event listeners calling three different methods, and all bound to the  ENTER_FRAME  event. In this case, we can keep the first event listener and get rid of the other two, then have the  drawParticles () method call  getStats() , which in turn calls  drawGraph() . Alternatively, we can simply create a new method that calls the  getStats(),  drawGraph()  and  drawParticles () for us directly, then have just one event listener that’s bound to the new method. The second option is more expensive however, so we’ll stick with the first.

// this line is added before the end of the <code> drawParticles </code>() method
getStats();
// this line is added before the end of the <code> getStats() </code> method
drawGraph();


We also remove the event parameter (which holds the Event object) from both the  drawGraph()  and  getStats() , as they are no longer needed.

## Step 18: Disable All Mouse Events for Display Objects That Do Not Need It

Since this Flash animation does not require any user interaction, we can free its display object from dispatching unnecessary mouse events. In the  Flames  class, we do that by setting its  mouseEnabled  property to false. We also do the same for all its children by setting the  mouseChildren  property to false. The following lines are added to the  Flames  constructor:

mouseEnabled = false;
mouseChildren = false;


## Step 19: Use the Graphics.drawPath() Method to Draw Complex Shapes

The  Graphics.drawPath()  is optimized for performance when drawing complex paths with many lines or curves. In the  Flames.drawGraph() method, the CPU load and memory consumption graph lines are both drawn using a combination of  Graphics.moveTo()  and  Graphics.lineTo()  methods.

for( var i = 0; i < memoryLogLength; i++ ){
ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
xpoint = (i / sampleSize) * graphWidth;
if( i == 0 ){
graphMC.graphics.moveTo(  xpoint, -ypoint );
continue;
}
graphMC.graphics.lineTo( xpoint, -ypoint );
}


We replace the original drawing methods with calls to  Graphics.drawPath(). An added advantage of the revised code below is that we also get to remove the drawing commands from the loops.

var commands:Vector.<int> = new Vector.<int>();
var data:Vector.<Number> = new Vector.<Number>();

for( var i = 0; i < memoryLogLength; i++ ){
ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
xpoint = (i / sampleSize) * graphWidth;

if( i == 0 ){
data[ data.length ] = xpoint;
data[ data.length ] = -ypoint;
commands[ commands.length ] = 1;
}

data[ data.length ] = xpoint;
data[ data.length ] = -ypoint;
commands[ commands.length ] = 2;
}
graphMC.graphics.drawPath( commands, data );


## Step 20: Make the Classes Final

The  final  attribute specifies that a method cannot be overridden or that a class cannot be extended. It can also make a class run faster, so we’ll make both the  Flames  and  Particle  classes final.

Edit: Reader Moko pointed us to this great article by Jackson Dunstan, which remarks that the final keyword does not actually have any effect on performance.

The CPU load is now 33.3%, while the total memory used stays between 4.8 and 5MB. We’ve come a long way from the CPU load of 41.7% and peak memory size of 9MB!

Which brings us to one of the most important decisions to be made in an optimization process: knowing when to stop. If you stop too early, your game or application may perform poorly on low end systems, and if you go too far, your code may get more obfuscated and harder to maintain. With this particular application, the animation looks smooth and fluid while CPU and memory usage are under control, so we’ll stop here.

## Summary

We have just looked at the process of optimization, using the Flames class as an example. While the many optimization tips were presented in a step by step fashion, the order doesn’t really matter. What’s important is being aware of the many issues that can slow down our program, and taking measures to correct them.

But remember to watch out for premature optimization; focus first on building your program and making it work, then start tweaking performance.

Patrick Jaoko is pjtops on Codecanyon
• http://mokogames.com/ Moko

Thanks for tutorial taht covers different ways of optimization.

• http://mokogames.com/ Moko

“The final attribute specifies that a method cannot be overridden or that a class cannot be extended.
It can also make a class run faster”

Did you check this?

http://jacksondunstan.com/articles/1774 :
“So, when looking for ways to improve the performance of your AS3 app, don’t waste your time on the const and final keywords.

• http://michaeljameswilliams.com/ Michael James Williams

Oh, nice find, thank you; I’ve amended the article.

• http://mokogames.com/ Moko

And one more I can’t understand – on last screenshot memory increasing constantly, but one screenshot befor memory consumption almost straight horizontal line.
Maybe time on last screenshot could be prolonged, to see that we don’t have memory leaks.

• Patrick Jaoko

Hi Moko,
Thanks for the feedback!
The site you linked to has some great optimisation tips, its was one of the many I used a lot when checking & researching for the article. The interesting thing to remember about AS3 development, is that the flash player itself and the AS3 languange is constantly changing, and many of these changes bring perfomance improvements. A good example being using int vs Number – a few years ago using int instead of Number for numeric data-types showed no improvements in perfomance, even though theoritically int was supposed to be faster than Number. Later versions of flash player optimised this data-type to perfom as it should, meaning an optimisation technique of little benefit back then, now actually runs faster on the latest flash player. So just like “int vs Number”, classes marked final should theoretically perfom faster, just like they do in other languanges like Java. Future version of the Flash Player may optimise final classes, or maybe they won’t, but I’ll keep using the technique anyway.

• actixn

CPU load = ( target frame rate – actual frame rate ) / actual frame rate * 100
should be
CPU load = ( target frame rate – actual frame rate ) / target frame rate * 100

This confused me for a while

• http://msfx.co.uk MSFX

There are still several points where you could optimize further still, the following talk (slides) from Grant Skinner makes a VERY good read…

http://gskinner.com/talks/quick/

• jackie young

Hi could you tell me how to download the slides of Grant Skinner

• http://michaeljameswilliams.com/ Michael James Williams

You might be able to do so here.

• http://www.funkcje.net DiviX

Great article I am definitely going to follow these guidelines.
Cheers