Finally An Article About The Finally Keyword
Try as I might, I just couldn’t find any articles about AS3’s finally
keyword. Sure I found Adobe’s documentation, but it seems no one is commenting any further about finally
. So today I’ll tackle the performance of what seems to be a straightforward keyword. Could it possibly cause a slowdown? Read on to find out!
As a refresher, the finally
keyword is used to hold code that should execute whether or not an exception in a try
block is caught by any of the catch
blocks. Basically, you can replace this:
try { throw "err"; } catch (str:String) { doStuff(); throw "another"; } doStuff();
With this code that uses finally
to eliminate the duplicated doStuff
code:
try { throw "err"; } catch (str:String) { throw "another"; } finally { doStuff(); }
So what if you simply added an empty finally
block? Would that cause any slowdown? It has no code at all so it shouldn’t, but strange things sometimes happen with Flash’s compiler, JIT, and VM, so let’s put it to the test to see if everything checks out:
package { import flash.utils.getTimer; import flash.display.Sprite; import flash.display.StageAlign; import flash.display.StageScaleMode; import flash.text.TextField; import flash.text.TextFieldAutoSize; public class FinallyKeyword extends Sprite { private var __logger:TextField = new TextField(); private function row(...cols): void { __logger.appendText(cols.join(",")+"\n"); } public function FinallyKeyword() { stage.align = StageAlign.TOP_LEFT; stage.scaleMode = StageScaleMode.NO_SCALE; __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); init(); } private function init(): void { var beforeTime:int; var afterTime:int; var REPS:int = 1000000; var i:int; row("Test", "Time"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { testFinally(); } afterTime = getTimer(); row("finally", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { testNoFinally(); } afterTime = getTimer(); row("no finally", (afterTime-beforeTime)); } private function testFinally(): void { try { try { throw "err"; } catch (str:String) { throw "err2"; } finally { } } catch (str2:String) { } finally { } } private function testNoFinally(): void { try { try { throw "err"; } catch (str:String) { throw "err2"; } } catch (str2:String) { } } } }
I ran this test app in the following environment:
- Flex SDK (MXMLC) 4.6.0.23201, compiling in release mode (no debugging or verbose stack traces)
- Release version of Flash Player 11.4.402.265
- 2.3 Ghz Intel Core i7
- Mac OS X 10.8.2
And here are the results I got:
Test | Time |
---|---|
finally | 1445 |
no finally | 1156 |
What!? We simply added a finally
block with no code in it and managed to slow down the test by 25%! So while finally
can (arguably) help clean up the style of your code, it certainly comes with a hefty overhead. Use it only outside of performance critical code. Then again, you really shouldn’t be using try/catch
for performance critical code if you can help it.
Spot a bug? Have a suggestion? Post a comment!
#1 by makc on October 1st, 2012 ·
I think I vaguely remember someone complaining that “finally” construct is implemented using otherwise invalid bytecode (i.e. it detects “finally” statements block based on in-validity of opcodes). maybe this approach is what responsible for slow-down.
#2 by Henke37 on October 1st, 2012 ·
@makc Doesn’t the player have a separate validation pass?
#3 by makc on October 1st, 2012 ·
idk, just a thought. couldn’t find a source I had in mind; the avm spec says this:
> A finally clause is normally translated as an exception handler that accepts any type of value, which it catches and rethrows after the body of the finally block finishes executing.
so anyway it has to do something every time even if it’s empty, I guess.
#4 by makc on October 1st, 2012 ·
here is another mention of invalid bytecode caused by finally block http://stackoverflow.com/questions/8841456/why-does-the-flash-actionscript3-compiler-emit-unnecessary-code