Casting Questions Answered
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 |
The failures dwarf the successes, so here they are alone:
Now here’s everything but with Y capped to “zoom in” on the fast results:
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.
#1 by makc on August 22nd, 2011 ·
it’s no longer possible to remember what is faster. We need plugins to IDEs to suggest better code.
#2 by jackson on August 22nd, 2011 ·
What a great idea! I suggest you forward it to the FDT, FlashDevelop, and FlashBuilder teams.
#3 by Henke37 on August 22nd, 2011 ·
Something that makes me wonder is the direction of the cast and the relative type difference. A base class to very derived class vs a very derived class from a base class.
#4 by jackson on August 22nd, 2011 ·
You, sir, have the very first topic for the next casting article. :)
#5 by BlooDHounD on August 22nd, 2011 ·
casting vs converting …
#6 by BlooDHounD on August 22nd, 2011 ·
casting win
#7 by jackson on August 22nd, 2011 ·
For more on converting, see Explicit Type Conversion.
#8 by BlooDHounD on September 23rd, 2011 ·
it was sarcasm. your tests compare cakes and flour.
#9 by skyboy on August 22nd, 2011 ·
Caching the constructor function for the classes may level the playing field, though may strip any benefit due to nJIT being unable to recognize it.It may also make no difference if nJIT performs constant unrolling.
var/const may perform differently under certain circumstances; even if only under the hood. I can’t say anything definitively either way, though; I haven’t looked closely enough at nJIT’s code to understand it, but I have been told by a trusted source it does perform it. If that’s the case, the constant expressions may be slowed down due to pulling from the constant pool while const/var are inlined as native code.