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 constwhen 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          	L4I’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          	L6The 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?
- constprovides helpful compile-time checking
- constis not faster than- var
- Using a compile-time constant is slower than a local varorconst
#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
varandconst.#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:
varis faster thanconstroughly half the timevarandconstis minorSo I don’t think these results show
constbeing 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.