- Nov
- 30th
- 2008
Why Statics in ActionScript 2.0 Are Slow
flash, flash lite, performance tuningMy design for the central class was pretty straightforward, with a single movieclip processing a list of live tweens and the standard functionality to run tweens on any property of a MovieClip, add callbacks, pause etc.
After I was done I started profiling (painful as it is in ActionScript 1.0/2.0) to discover any obvious bottlenecks. One of the first things I noticed was the length of time a simple if/else if/else block was taking to process when constructing a tween.
....
else if (easeDirection & Ease.OUT)
....
I was somewhat puzzled and resorted to flasm‘ing the offending method to see what was going on under the hood. What I found surprised me to say the least.
When a static property is referenced its entire package chain is pushed onto the stack before it can be accessed.
So if you have a static property under the Ease class, Ease.IN and this sits in the package co.uk.mikestead.animate then co, uk, mikestead and animate (package chains are mirrored as objects under _global) are pushed onto the stack before the Ease class (type function) is pushed on, to get at IN. This occurs for every access, even if the same static property has been accessed previously within the same method.
Now this is not good, but if you’re accessing a single static property in your method then it’s a hit you can probably get away with (I’ll describe shortly how you can even minimize this). If however you are accessing one or more static properties of a class then here’s how you can reduce the overhead.
If you access one or more static properties of a class within a method then store a reference to the class at the start of the method and access the static properties through this.
By doing this the package chain is traversed just once each time the method is called, with the class then referenced through this variable for the life of the method execution.
To provide an example, and show how much of a performance gain this can make, I’ve put together a simple test movie with the following code.
class StaticLookupSpeedTest
{
/** Number of iterations per test */
public var iterations:Number = 100000;
/** Reference to Ease class */
private var easeType:Function;
/**
* Class constructor.
*/
public function StaticLookupSpeedTest()
{
easeType = Ease;
}
/**
* Test the lookup speed of static properties of an external class.
*
* @return The execution time of this test in milliseconds
*/
public function testStaticLookup():Number
{
var i:Number = iterations;
var t:Number;
var time:Number = getTimer();
while (i--)
{
t = Ease.EASE_IN;
t = Ease.EASE_OUT;
}
return getTimer() - time;
}
/**
* Test the lookup speed of static properties of an external
* class which is referenced locally in a method.
*
* @return The execution time of this test in milliseconds
*/
public function testStaticLookupMethodLevel():Number
{
var ease:Function = Ease;
var i:Number = iterations;
var t:Number;
var time:Number = getTimer();
while (i--)
{
t = ease.EASE_IN;
t = ease.EASE_OUT;
}
return getTimer() - time;
}
/**
* Test the lookup speed of static properties of an external
* class which is referenced locally from class level field.
*
* @return The execution time of this test in milliseconds
*/
public function testStaticLookupClassLevel():Number
{
var i:Number = iterations;
var t:Number;
var time:Number = getTimer();
while (i--)
{
t = easeType.EASE_IN;
t = easeType.EASE_OUT;
}
return getTimer() - time;
}
}
And here’s the results of the first two tests run under Flash Player 9.
- External static prop lookup: 694ms
- Static prop lookup through class reference in method: 140ms
And with Flash Lite 2.1 under the Sony Ericsson W51S emulator.
- External static prop lookup: 1208ms
- Static prop lookup through class reference in method: 264ms
In both tests the local ref provides a 4.5+ speed increase.
You may be thinking this is no big deal, but if you have many references to static properties scattered around your application then this can add up significantly.
As I mentioned earlier, there’s a way to optimize even the package traversal at the start of a method, and you may have noticed it from the third and final test in the class.
If you access one or more static properties throughout a class (even static properties of the class you’re in!) you should store a reference to this class as a field and use it to access those same static properties within your class.
This has a similar effect as the method level version, but now the traversal is just done once each time your class is instantiated and then held in scope for the life of your object.
The result run under Flash Player 9.
- Static prop lookup through class reference in class: 197ms
And with Flash Lite 2.1 under the Sony Ericsson W51S emulator.
- Static prop lookup through class reference in class: 422ms
You’ll notice this result is slightly slower than the local method version. This is because each static access requires the class level reference variable to be pushed onto the stack. i.e. push easeType, push IN where as the method version just uses push IN once it has the class in scope.
As with most optimizations there are of course downsides. The primary one here being loss of static (as in compile time) type checking. By optimizing at class level your loss of type checking is more prevalent making debugging potentially more tricky, while at method level this is less of an issue. This is one reason I tend to optimize at the method level first, and then if need be at the class level, remembering to provide clear comments too.
I should make clear that on the web this optimization is not needed much of the time, but on the mobile it’s something which should be thought about throughout development.
One last area I’d like to touch upon is that of obfuscation. Obfuscators are used to pre-process and transform code into a less human readable, and often more optimized, version of the original. For example an package chain of co.uk.mikestead.animate may be transformed into something like a33se7ef_. Not only does this reduce memory usage by cutting down object count but it also increases execution speed by reducing variable name size (on the whole). The end swf generated is also likely to be lighter because of this.
Great you’re thinking! The answer to all our problems. Well yes and no. Yes these are very useful but only if they do what they say on the tin. Tracking bugs down introduced during this preprocessing stage is EXTREMELY tricky. Performing obfuscation on a dynamic language also introduces limitations during development as dynamically accessing a type will not be picked up by any obfuscater I’ve come across.
So my advice is to be wary introducing this technology into your tool-set. Perform your own thorough testing before making any decision and be sure to retrospect regularly to ensure the benefits are outweighing the cons.
You can grab the example test code and flasm output here.
My next post will continue on this topic describing other considerations to be taken into account when dealing with statics.
2 Comments
Jump to comment form | comments rss [?] | trackback uri [?]