The Const Keyword
Amazingly, I’ve never covered the const
keyword, but a couple of recent comments have prompted me to cover the subject in depth with today’s article.
There’s much to be excited about with const
:
- Safety– the compiler will give you an error if you don’t initialize your
const
when declaring it or you assign to it later - Performance– a good way to tell the compiler and JIT that it can apply a bunch of optimizations like constant folding
Unfortunately, older versions of MXMLC and Flash Player only enforced const
at compile time and did nothing for performance. Has anything changed now that we have MXMLC 4.1 and Flash Player 10.1? To investigate this, consider the following two functions:
private function testVar(): int { var val:int = 3; return val + val; } private function testConst(): int { const val:int = 3; return val + val; }
Here’s how they get compiled by MXMLC 4.1.0.16076 (the latest stable version as of this writing):
function private::testVar():int /* disp_id 0*/ { // local_count=2 max_scope=1 max_stack=2 code_len=9 0 getlocal0 1 pushscope 2 pushbyte 3 4 setlocal1 5 getlocal1 6 getlocal1 7 add 8 returnvalue } function private::testConst():int /* disp_id 0*/ { // local_count=2 max_scope=1 max_stack=2 code_len=9 0 getlocal0 1 pushscope 2 pushbyte 3 4 setlocal1 5 getlocal1 6 getlocal1 7 add 8 returnvalue }
As you can see, these functions are compiled to exactly the same bytecode. There is no constant folding going on here, at least in bytecode. Nevertheless, let’s see if some intensive usage of a const
variable improves performance:
package { import flash.display.*; import flash.utils.*; import flash.text.*; public class ConstTest extends Sprite { private var __logger:TextField = new TextField(); private function log(msg:*): void { __logger.appendText(msg + "\n"); } public function ConstTest() { __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); var i:int; var beforeTime:int; var afterTime:int; const REPS_CONST:int = 100000000; var REPS:int = REPS_CONST; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { } afterTime = getTimer(); log("var: " + (afterTime-beforeTime)); REPS = REPS_CONST; beforeTime = getTimer(); for (i = 0; i < REPS_CONST; ++i) { } afterTime = getTimer(); log("const: " + (afterTime-beforeTime)); } } }
Each loop is compiled to the same bytecode:
95 pushbyte 0 97 setlocal1 98 jump L3 L4: 102 label 103 inclocal_i 1 L3: 105 getlocal1 106 getlocal 4 108 iflt L4
I’ve thrown in a re-assignment of the REPS
variable to make the JIT’s optimization job harder. This test is about half var
and half const
. While that will cut const
‘s supposed performance advantages in half, they should still be apparent with enough iterations. Let’s try 100 million with Flash Player 10.1.85.3:
Environment | Var | Const |
---|---|---|
2.4 Ghz Intel Core i5, Mac OS X | 215 | 215 |
This shows that const
and var
run at the same speed. The JIT has therefore not optimized const
. So, if const
can’t improve performance, can compile-time constants?. Consider the following addition:
package { import flash.display.*; import flash.utils.*; import flash.text.*; public class ConstTest2 extends Sprite { private var __logger:TextField = new TextField(); private function log(msg:*): void { __logger.appendText(msg + "\n"); } public function ConstTest2() { __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); var i:int; var beforeTime:int; var afterTime:int; const REPS_CONST:int = TEST::REPS_DEFINE; var REPS:int = REPS_CONST; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { } afterTime = getTimer(); log("var: " + (afterTime-beforeTime)); REPS = REPS_CONST; beforeTime = getTimer(); for (i = 0; i < REPS_CONST; ++i) { } afterTime = getTimer(); log("const: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < TEST::REPS_DEFINE; ++i) { } afterTime = getTimer(); log("define: " + (afterTime-beforeTime)); } } }
This is compiled with the same number of loop repetitions:
mxmlc -define=TEST::REPS_DEFINE,100000000 ConstTest2.as
The loop bytecode for this version looks like this:
142 pushbyte 0 144 setlocal1 145 jump L5 L6: 149 label 150 inclocal_i 1 L5: 152 getlocal1 153 pushint 100000000 // 0x5f5e100 155 iflt L6
The only difference is that the getlocal 4
access of the var
/const
has been replaced by pushint 100000000
pushing the literal value. So we’ve succeeded in eliminating the local variable/constant, but has it helped? Let’s see:
Environment | Var | Const | Define |
---|---|---|---|
2.4 Ghz Intel Core i5, Mac OS X | 215 | 215 | 241 |
Pushing the constant is 12% more expensive than accessing the local variable or constant! So what have we found today?
const
provides helpful compile-time checkingconst
is not faster thanvar
- Using a compile-time constant is slower than a local
var
orconst
#1 by Amyconstants on November 1st, 2010 ·
Typically, constants are not defined at the method level, but at the Class level. What happens if you test the way people would actually use it?
#2 by jackson on November 1st, 2010 ·
I still don’t get a difference between
var
andconst
.#3 by luke on February 20th, 2013 ·
just what i was thinking lol
#4 by skyboy on November 1st, 2010 ·
I’m not sure what the difference is between our machines, but for some reasons constants are, on average, faster than vars. Also, there is some constant unrolling taking place, just not significantly.
http://pastebin.com/MRDTWdKQ
My version of the test and the bytecode it compiles to.
#5 by as3isolib on November 1st, 2010 ·
Maybe this is not well known but the Mac Flash Player performs much more poorly than its PC counterpart. During optimizing of my code I have found that the Flash Player on the Mac has consistently pushed poorer results than on a PC of comparable stats. Normally that’s fine for me as I am able to target the worst-case scenario, making the assumption that the PC results will be much better which will target a wider audience.
#6 by jackson on November 1st, 2010 ·
This is, sadly, often the case. However, it doesn’t seem to be the case with this article as I found in my comment below.
#7 by jackson on November 1st, 2010 ·
I ran your modified test a few dozen times on my 2.8 Ghz Intel Xeon with Windows 7 and my 2.0 Ghz Intel Core 2 Duo with Mac OS X 10.5 and my results roughly match yours. However, our results show two key trends:
var
is faster thanconst
roughly half the timevar
andconst
is minorSo I don’t think these results show
const
being statistically significantly faster thanvar
.#8 by skyboy on November 1st, 2010 ·
Perhaps later this will change, but for now it does have one key advantage: you can’t assign values to it after it’s declared, for things like a list of children, this is best, though you probably still want to keep that unexposed and let methods handle it, so children can’t be removed (by malicious 3rd-party content, or accidentally) from that list but remain in the display list.
#9 by PseudoSal on April 30th, 2013 ·
Thanks Jackson! You’re analysis on optimization and the syntax tuts are the best I’ve found so far! Very useful, easy to read. *****
#10 by magbetjke on August 1st, 2013 ·
What about ASC 2?
#11 by jackson on August 1st, 2013 ·
I just tried it again and get the same performance results with Flash Player 11.8 on Windows 7 regardless of compiling with ASC 1 or ASC 2.
#12 by Steve D on October 24th, 2014 ·
Another disadvantage of using const is that the debugger can’t evaluate it, it only shows vars.