One of the first articles I wrote for this site covered the two types of casts available to the AS3 programmer. In that article I covered the syntax of the two as well as some of the quirks. Today I’ll cover the performance differences between them.

Jonnie Hallman left a comment on the first article claiming that the as operator (e.g. obj as BitmapData) is 4.5x faster than the function call style (e.g. BitmapData(obj)). We’ll see later if he’s right, but for now let’s take a look at a couple of functions demonstrating the two approaches:

private function functionCallStyle(bmd:BitmapData): void
{
	BitmapData(bmd);
}

MXMLC 4.1 compiles the above to this bytecode:

  function private::functionCallStyle(flash.display::BitmapData):void	/* disp_id 0*/
  {
    // local_count=2 max_scope=1 max_stack=2 code_len=9
    0       getlocal0     	
    1       pushscope     	
    2       findpropstrict	flash.display::BitmapData
    4       getlocal1     	
    5       callpropvoid  	flash.display::BitmapData (1)
    8       returnvoid    	
  }

This is quite straightforward:

  • findpropstrict – Look up the BitmapData class
  • getlocal1 – Get the bmd variable
  • callpropvoid – Do the cast

Now let’s look at the as operator version:

private function asKeyword(bmd:BitmapData): void
{
	bmd as BitmapData;
}

And here’s the bytecode generated for this version:

  function private::asKeyword(flash.display::BitmapData):void	/* disp_id 0*/
  {
    // local_count=2 max_scope=1 max_stack=2 code_len=8
    0       getlocal0     	
    1       pushscope     	
    2       getlocal1     	
    3       getlex        	flash.display::BitmapData
    5       astypelate    	
    6       pop           	
    7       returnvoid    	
  }

This version involves one more instruction, but it also pretty simple:

  • getlocal1 – Get the bmd variable
  • getlex BitmapData – Find the BitmapData type
  • astypelate – Do the as cast
  • pop – Discard the resulting value (a BitmapData or null)

It’s not clear which version is faster from just the bytecode, so let’s do a performance test:

package
{
	import flash.display.*;
	import flash.text.*;
	import flash.utils.*;
 
	public class CastSpeed extends Sprite
	{
		public function CastSpeed()
		{
			var logger:TextField = new TextField();
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
			function log(msg:*): void { logger.appendText(msg+"\n"); }
 
			const ITERATIONS:int = 5000000;
			var i:int;
			var beforeTime:int;
			var afterTime:int;
 
			var bmd:BitmapData = new BitmapData(1, 1);
			var spr:Sprite = new Sprite();
 
			log("Cast succeeds:");
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				BitmapData(bmd);
			}
			afterTime = getTimer();
			log("\tFunction call style: " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				bmd as BitmapData;
			}
			afterTime = getTimer();
			log("\tAs keyword: " + (afterTime-beforeTime));
 
			log("Cast fails:");
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					BitmapData(spr);
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			log("\tFunction call style: " + (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				spr as BitmapData;
			}
			afterTime = getTimer();
			log("\tAs keyword: " + (afterTime-beforeTime));
		}
	}
}

And here are the results with Flash Player 10.1

Environment Cast Succeeds Cast Fails
Function Call Style As Keyword Function Call Style As Keyword
3.0 Ghz Intel Core 2 Duo, Windows XP 149 38 9238 39
2.0 Ghz Intel Core 2 Duo, Mac OS X 256 57 18245 58

Now it’s clear which version is faster. Even when the cast succeeds, the as keyword is about 4x-4.5x faster than the function call approach. This is right in line with Jonnie Hallman’s comment on the last article, even with the introduction of Flash Player 10.1. Still, it’s good to know about the slightly better performance on Windows and even better to know about the radically worse performance due to the try/catch required by the function call version’s error throwing, as Robert Penner pointed out in a comment. That penalty results in a cast that’s 236x-314x slower than the equivalent as operator approach!

You might want to review any performance critical code to see if you have any function call casting in there, especially if you expect the cast to fail!