I’ve heard from several sources that code in a constructor executes slower than code outside a constructor. So I decided to do a test. How much slower are constructors?
The methodology for the test is to run some expensive code in a constructor function and the same expensive code in a non-constructor function. This code is duplicated and not factored out to ensure that the the constructor function is doing the work directly rather than indirectly via a function call. Normally, you should factor this out, but here it would defeat the purpose of the test.
The work I’ve chosen to do is a big loop full of addition. The result of the computation is pointless, but stored in a public variable just to make sure that the compiler will not optimize out the loop, even though it probably wouldn’t anyhow.
Without further ado, here is the test app:
package { import flash.text.*; import flash.events.*; import flash.display.*; public class ConstructorTest extends Sprite { private var __logger:TextField = new TextField(); public function ConstructorTest() { __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); // Do the next frame so we don't test code in functions called FROM // a constructor, just in case. addEventListener(Event.ENTER_FRAME, onEnterFrame); } private function onEnterFrame(ev:Event): void { // We just wanted to do the test once removeEventListener(Event.ENTER_FRAME, onEnterFrame); // Constructor test var tester:Tester = new Tester(log); // Non-constructor test tester.init(log); } private function log(msg:*): void { __logger.appendText(msg + "\n"); } } } import flash.utils.*; class Tester { public var total:int; public function Tester(log:Function) { var total:int = 0; var beforeTime:int = getTimer(); for (var i:int; i < 50000; ++i) { for (var j:int = 0; j < i; ++j) { total += j; } } log("constructor time: " + (getTimer()-beforeTime)); this.total = total; } public function init(log:Function): void { var total:int = 0; var beforeTime:int = getTimer(); for (var i:int; i < 50000; ++i) { for (var j:int = 0; j < i; ++j) { total += j; } } log("init() time: " + (getTimer()-beforeTime)); this.total = total; } }
The output I get on a 2.2Ghz Intel Core 2 Duo with 2GB of RAM on Mac OS X 10.6 (Snow Leopard) is:
constructor time: 3446 init() time: 3437
The results I get on a 3.0Ghz Intel Core 2 Duo with 2GB of RAM on Windows XP SP3 are:
constructor time: 2518 init() time: 2519
These results seem to match, except for overall scale. While the init() function can sometimes be a tiny bit faster than the constructor, it is indeed a tiny bit. Unless you are doing almost all of your time-critical work in constructors, I don’t think you have anything to worry about.
#1 by Alec McEachran on September 16th, 2009 · | Quote
Thanks for the interesting article.
I’m not sure you can call that a performance improvement because the margin of error is so slim; you’d need a much bigger sample of constructors to have any confidence.
As a performance-irrelevant aside, I have two reasons to favour init over the constructor:
1. You can call super.init(…) from anywhere in a sub-class’s init method;
2. Once you construct a super-class with parameters the compiler commits you to writing out that constructor for any sub-class.
They’re good enough to prefer init over the constructor when no other overriding reason presents itself, in my opinion.
Regards, Alec.
#2 by jackson on September 16th, 2009 · | Quote
Thanks for the comment. The point of the article was to show that there is no performance gain or loss either way. It simply doesn’t matter if the code is in the constructor or an init() function. I did try a few other types of constructors, including some making calls to more native-heavy code like Math.sqrt(), and saw no difference, so I posed just this.
I agree about when to use init() over constructors. Object re-use is a big deal when trying to eliminate GC slowdown and allocation time. It also allows free lists, as today’s article points out. Personally, I like to do just as much in the constructor as is necessary for the object to be constructed in a valid, known state, per the RAII approach. Then the user should call init() or other functions to further set up the object as required.
Thanks again for the comment.