Object Creation: Part II
As a followup to the previous article about object creation and a comment about an alternate object creation strategy, today’s article will expand the coverage of object creation. I will also discuss the performance (and generated bytecode) for creating non-empty objects to see if there are any redeeming factors to the “curly braces” (o = {}
) approach.
As the comment said, you can create an empty Object
by using the global Object()
cast function to cast null
to an Object
: o = Object(null);
. While obscure, this is a valid approach. Let’s look at a test function and the bytecode generated for it by MXMLC 4:
public function objectCastEmpty(): void { var o:Object = Object(null); }
function objectCastEmpty():void /* disp_id 0*/ { // local_count=2 max_scope=1 max_stack=2 code_len=12 0 getlocal0 1 pushscope 2 findpropstrict Object 4 pushnull 5 callproperty Object (1) 8 coerce Object 10 setlocal1 11 returnvoid }
This approach begins with findpropstrict
like the new
operator approach but then uses pushnull
and callproperty Object(1)
to call the global cast function. We’ll see later in this article if that’s actually faster than the new
operator’s constructprop
approach. For now, let’s look at some functions for creating a new Object
with properties starting with the “curly braces” approach:
public function curlyBraces5(): void { var o:Object = {a:100,b:200,c:300,d:400,e:500}; }
function curlyBraces5():void /* disp_id 0*/ { // local_count=2 max_scope=1 max_stack=10 code_len=32 0 getlocal0 1 pushscope 2 pushstring "a" 4 pushbyte 100 6 pushstring "b" 8 pushshort 200 11 pushstring "c" 13 pushshort 300 16 pushstring "d" 18 pushshort 400 21 pushstring "e" 23 pushshort 500 26 newobject {5} 28 coerce Object 30 setlocal1 31 returnvoid }
Here we see newobject
leveraged by passing the five properties to it. This is very terse, but we’ll see how it performs later on. For now, let’s look at the new
operator approach:
public function newOperator5assign(): void { var o:Object = new Object(); o.a = 100; o.b = 200; o.c = 300; o.d = 400; o.e = 500; }
function newOperator5assign():void /* disp_id 0*/ { // local_count=2 max_scope=1 max_stack=2 code_len=40 0 getlocal0 1 pushscope 2 findpropstrict Object 4 constructprop Object (0) 7 coerce Object 9 setlocal1 10 getlocal1 11 pushbyte 100 13 setproperty a 15 getlocal1 16 pushshort 200 19 setproperty b 21 getlocal1 22 pushshort 300 25 setproperty c 27 getlocal1 28 pushshort 400 31 setproperty d 33 getlocal1 34 pushshort 500 37 setproperty e 39 returnvoid }
This approach is longer as it is forced to use getlocal1
over and over to set up the assignment via pushshort
and setproperty
instructions. How about if we used a with
block?
public function newOperator5with(): void { var o:Object = new Object(); with (o) { a = 100; b = 200; c = 300; d = 400; e = 500; } }
function newOperator5with():void /* disp_id 0*/ { activation { var o:Object /* slot_id 1 */ } // local_count=3 max_scope=3 max_stack=2 code_len=62 0 getlocal0 1 pushscope 2 newactivation 3 dup 4 setlocal1 5 pushscope 6 getscopeobject 1 8 findpropstrict Object 10 constructprop Object (0) 13 coerce Object 15 setslot 1 17 getscopeobject 1 19 getslot 1 21 dup 22 setlocal2 23 pushwith 24 findproperty a 26 pushbyte 100 28 setproperty a 30 findproperty b 32 pushshort 200 35 setproperty b 37 findproperty c 39 pushshort 300 42 setproperty c 44 findproperty d 46 pushshort 400 49 setproperty d 51 findproperty e 53 pushshort 500 56 setproperty e 58 popscope 59 kill 2 61 returnvoid }
This version is highly complex compared to the straightforward bytecode we’ve seen so far. We have newactivation
, setslot
, getslot
, getscopeobject
, findproperty
, and pushwith
in addition to the assignment version’s pushshort
/setproperty
pairs. Since we’ve seen the relevant “assign” and “with” approaches to filling in the empty objects created by the new
operator and Object
cast approaches, I’ll omit the AS3 and bytecode examples for those and move straight on to the performance test:
package { import flash.display.*; import flash.text.*; import flash.utils.*; /** * An app to test the speed of different ways to create an object * @author Jackson Dunstan (jacksondunstan.com) */ public class ObjectCreation extends Sprite { public function ObjectCreation() { var logger:TextField = new TextField(); logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); function log(msg:*): void { logger.appendText(msg+"\n"); } var i:int; var beforeTime:int; var afterTime:int; var o:Object; const REPS:int = 100000; log("empty"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = {}; } afterTime = getTimer(); log("\tCurly braces: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = new Object(); } afterTime = getTimer(); log("\tNew operator: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = Object(null); } afterTime = getTimer(); log("\tObject cast: " + (afterTime-beforeTime)); log("5 properties"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = {a:100,b:200,c:300,d:400,e:500}; } afterTime = getTimer(); log("\tCurly braces: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = new Object(); o.a = 100; o.b = 200; o.c = 300; o.d = 400; o.e = 500; } afterTime = getTimer(); log("\tNew operator (assign): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = Object(null); o.a = 100; o.b = 200; o.c = 300; o.d = 400; o.e = 500; } afterTime = getTimer(); log("\tObject cast (assign): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = new Object(); with (o) { a = 100; b = 200; c = 300; d = 400; e = 500; } } afterTime = getTimer(); log("\tNew operator (with): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { o = Object(null); with (o) { a = 100; b = 200; c = 300; d = 400; e = 500; } } afterTime = getTimer(); log("\tObject cast (with): " + (afterTime-beforeTime)); } } }
Note that the number of iterations has changed since the last version of this test. The results are:
Environment | Empty | 5 Properties | ||||||
---|---|---|---|---|---|---|---|---|
Curly Braces | New Operator | Object Cast | Curly Braces | New Operator (assign) | Object Cast (assign) | New Operator (with) | Object Cast (with) | |
3.0 Ghz Intel Core 2 Duo, Windows XP | 19 | 11 | 14 | 48 | 69 | 78 | 423 | 424 |
2.0 Ghz Intel Core 2 Duo, Mac OS X | 32 | 22 | 24 | 67 | 115 | 116 | 641 | 640 |
The results for empty objects are relatively the same as with the test from last time, just with lower numbers due to fewer iterations. While the “curly braces” approach is slowest to create an empty object, it’s fastest when creating an object with five properties. This “curly braces” approach likely overtakes the other techniques after 3-4 properties have been added and then widens its gap as you add more and more. Lastly, the “object cast” approach is roughly the same as the “new operator” approach but somewhat slower with empty object creation.
The conclusion at the end of the last article was to prefer the “new operator” approach over the “curly braces” approach when creating empty objects. This still holds, but reverse is true if you plan on adding a few properties. The “object cast” approach, however, holds no advantages in either scenario.
#1 by Mark on August 30th, 2010 ·
“This still holds, but reverse is true if you plan on adding a few properties”
Nice benchmark (again). I wonder in which case you want to create an object without adding some properties..?
#2 by jackson on August 30th, 2010 ·
It’s definitely more common to add some properties than to create an empty
Object
, but you may want to do so to avoid usingnull
as an element of a collection (e.g.Array
), a return value, a parameter, etc.#3 by bwhiting on September 6th, 2010 ·
2GHz 3.25GB Ram Core 2 Quad on XP
interesting differences between machines!
#4 by jackson on September 6th, 2010 ·
Thanks for posting your results. It pretty clearly demonstrates that AS3 execution is always single-threaded, so the quad core isn’t helping out here since the test is purely AS3. It can definitely help in other areas though, like with rendering and Pixel Bender kernels.
#5 by Alama on September 19th, 2010 ·
My résults on a old intel Northwood 2.8G single core HT. On XP pro 32 with 2G Ram DDR 400 Mhz. My résult is poor regardless your 3Gz core 2 duo .. :S Impressionant..
#6 by jackson on September 19th, 2010 ·
The important part is that your numbers are still relatively the same, except for the empty “object cast” test. This shows that the performance isn’t specific to a certain CPU but instead simply scales by overall CPU speed.
Thanks for posting your results!
#7 by Alama on September 19th, 2010 ·
I see the result on a recent cpu i7 i5 or 2.6 to 3 GHz. To motivate me to bring me up a new machine .. :D