You, my dear readers, have posed many fine questions and chimed in with many excellent suggestions to my previous articles on typecasting and today I will answer them! (for newcomers to this series, read on for tips showing how to easily speed up your casts by 200x or more)

Today’s update starts with the test from the last article and greatly improves it:

  • I now store the result of the cast (thanks to Till Schneidereit)
  • Added cast through untyped (*) variable (thanks to Skyboy)
  • Added casts for non-builtin (“custom”) classes (thanks to Keyston)
  • Added casts of classes deep in the inheritance tree (thanks to Keyston)

So, have a look at the updated performance test:

package
{
	import flash.display.*;
	import flash.text.*;
	import flash.utils.*;
 
	public class CastSpeedUpdate extends Sprite
	{
		private var logger:TextField = new TextField();
		private function row(...cols): void
		{
			logger.appendText(cols.join(",") + "\n");
		}
 
		public function CastSpeedUpdate()
		{
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			var beforeTime:int;
			var afterTime:int;
			var successTime:int;
			var failTime:int;
			const ITERATIONS:int = 1000000;
			var i:int;
 
			var bmd:BitmapData = new BitmapData(1, 1);
			var spr:Sprite = new Sprite();
			var my1:MyClass1 = new MyClass1();
			var my5:MyClass5 = new MyClass5();
			var myU:MyUnrelatedClass = new MyUnrelatedClass();
			var bmdResult:BitmapData;
			var sprResult:Sprite;
			var my1Result:MyClass1;
			var my5Result:MyClass5;
			var untyped:*;
 
			row("Cast Style-Depth-Object Type", "Success Time", "Failure Time");
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				bmdResult = BitmapData(bmd);
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					bmdResult = BitmapData(spr);
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Function call-1-Builtin", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				bmdResult = bmd as BitmapData;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				bmdResult = spr as BitmapData;
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("As keyword-1-Builtin", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				untyped = bmd;
				bmdResult = untyped;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					untyped = spr;
					bmdResult = untyped;
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Untyped-1-Builtin", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				sprResult = Sprite(spr);
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					sprResult = Sprite(myU);
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Function call-5-Builtin", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				sprResult = spr as Sprite;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				sprResult = myU as Sprite;
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("As keyword-5-Builtin", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				untyped = spr;
				sprResult = untyped;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					untyped = myU;
					sprResult = untyped;
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Untyped-5-Builtin", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				my1Result = MyClass1(my1);
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					my1Result = MyClass1(myU);
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Function call-1-Custom", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				my1Result = my1 as MyClass1;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				my1Result = myU as MyClass1;
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("As keyword-1-Custom", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				untyped = my1;
				my1Result = untyped;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					untyped = myU;
					my1Result = untyped;
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Untyped-1-Custom", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				my5Result = MyClass5(my5);
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					my5Result = MyClass5(myU);
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Function call-5-Custom", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				my5Result = my5 as MyClass5;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				my5Result = myU as MyClass5;
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("As keyword-5-Custom", successTime, failTime);
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				untyped = my5;
				my5Result = untyped;
			}
			afterTime = getTimer();
			successTime = afterTime - beforeTime;
 
			beforeTime = getTimer();
			for (i = 0; i < ITERATIONS; ++i)
			{
				try
				{
					untyped = myU;
					my5Result = untyped;
				}
				catch (err:TypeError)
				{
				}
			}
			afterTime = getTimer();
			failTime = afterTime - beforeTime;
			row("Untyped-5-Custom", successTime, failTime);
		}
	}
}
class MyClass1{}
class MyClass2 extends MyClass1{}
class MyClass3 extends MyClass2{}
class MyClass4 extends MyClass3{}
class MyClass5 extends MyClass4{}
class MyUnrelatedClass{}

I ran the performance test with the following environment:

  • Flex SDK (MXMLC) 4.5.1.21328, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 10.3.183.5
  • 2.4 Ghz Intel Core i5
  • Mac OS X 10.7.1

And got these results:

Cast Style-Depth-Object Type Success Time Failure Time
Function call-1-Builtin 54 2224
As keyword-1-Builtin 46 40
Untyped-1-Builtin 38 2238
Function call-5-Builtin 51 2244
As keyword-5-Builtin 44 40
Untyped-5-Builtin 37 2192
Function call-1-Custom 20 2193
As keyword-1-Custom 36 33
Untyped-1-Custom 34 2198
Function call-5-Custom 21 2180
As keyword-5-Custom 36 33
Untyped-5-Custom 35 2185

Cast Speed Performance Graph

The failures dwarf the successes, so here they are alone:

Cast Speed Performance Graph (success only)

Now here’s everything but with Y capped to “zoom in” on the fast results:

Cast Speed Performance Graph (capped)

Here are some takeaways:

  • Never fail the cast with the “function call style” or the “untyped variable” approach- the exception that’s thrown kills your performance.

  • Depth of subcasting seems irrelevant
  • The fastest casts are “function call style” on “custom” classes
  • “Custom” class casting is generally faster than “built-in” class casting
  • Other than the above points, there is little difference between styles

Know something more about casting? Spot a bug? Post a comment below. As you’ve just seen, it may well be answered in-depth.