If-Else Trees vs. Array and Vector
We’ve seen that if-else trees are way faster than Object, Dictionary, and even switch at key-value mapping, but how do they stack up against Array and Vector? Today’s article puts them to the test and uncovers some unexpected results.
Let’s be clear up front that this isn’t really an “apples to apples” test. Both Array and Vector require integer keys. Well, technically Array doesn’t because it’s a dynamic class and you can therefore add arbitrary fields to it like you can with Object or even integer keys beyond the contiguous sequence at the start. However, if you want the full speed benefits you’ll want to use a densely-packed Array. You’ll also need to make sure your keys are contiguous integers starting at zero. This is a lot more restrictive than Object, Dictionary, switch, and if-else trees which can all include keys that are not contiguous integers starting at zero.
Regardless of these differences there are still many cases where you can use contiguous integer keys starting at zero, so let’s put Array and Vector to the test against all of the previous contestants:
ObjectDictionary(weak keys)Dictionary(strong keys)ArrayVector(dynamic length)Vector(fixed length)if-elsetreesswitch
package { import flash.utils.Dictionary; import flash.text.TextField; import flash.utils.getTimer; import flash.display.Sprite; public class IfElseTreesVsObjectDictionary extends Sprite { public function IfElseTreesVsObjectDictionary() { init(); } private function init(): void { var beforeTime:int; var afterTime:int; var i:int; var resInt:int; var object:Object = new Object(); var dictionaryWeak:Dictionary = new Dictionary(true); var dictionaryStrong:Dictionary = new Dictionary(false); var array:Array = new Array(); var vector:Vector.<int> = new Vector.<int>(); var vectorFixed:Vector.<int> = new Vector.<int>(true); var REPS:int = 1000000; for (i = 0; i < 20; ++i) { object[i] = i; dictionaryWeak[i] = i; dictionaryStrong[i] = i; array[i] = i; vector[i] = i; vectorFixed[i] = i; } var out:String = "Map,Time\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = object["0"]; resInt = object["1"]; resInt = object["2"]; resInt = object["3"]; resInt = object["4"]; resInt = object["5"]; resInt = object["6"]; resInt = object["7"]; resInt = object["8"]; resInt = object["9"]; resInt = object["10"]; resInt = object["11"]; resInt = object["12"]; resInt = object["13"]; resInt = object["14"]; resInt = object["15"]; resInt = object["16"]; resInt = object["17"]; resInt = object["18"]; resInt = object["19"]; } afterTime = getTimer(); out += "Object," + (afterTime-beforeTime) + "\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = dictionaryWeak[0]; resInt = dictionaryWeak[1]; resInt = dictionaryWeak[2]; resInt = dictionaryWeak[3]; resInt = dictionaryWeak[4]; resInt = dictionaryWeak[5]; resInt = dictionaryWeak[6]; resInt = dictionaryWeak[7]; resInt = dictionaryWeak[8]; resInt = dictionaryWeak[9]; resInt = dictionaryWeak[10]; resInt = dictionaryWeak[11]; resInt = dictionaryWeak[12]; resInt = dictionaryWeak[13]; resInt = dictionaryWeak[14]; resInt = dictionaryWeak[15]; resInt = dictionaryWeak[16]; resInt = dictionaryWeak[17]; resInt = dictionaryWeak[18]; resInt = dictionaryWeak[19]; } afterTime = getTimer(); out += "Dictionary (weak)," + (afterTime-beforeTime) + "\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = dictionaryStrong[0]; resInt = dictionaryStrong[1]; resInt = dictionaryStrong[2]; resInt = dictionaryStrong[3]; resInt = dictionaryStrong[4]; resInt = dictionaryStrong[5]; resInt = dictionaryStrong[6]; resInt = dictionaryStrong[7]; resInt = dictionaryStrong[8]; resInt = dictionaryStrong[9]; resInt = dictionaryStrong[10]; resInt = dictionaryStrong[11]; resInt = dictionaryStrong[12]; resInt = dictionaryStrong[13]; resInt = dictionaryStrong[14]; resInt = dictionaryStrong[15]; resInt = dictionaryStrong[16]; resInt = dictionaryStrong[17]; resInt = dictionaryStrong[18]; resInt = dictionaryStrong[19]; } afterTime = getTimer(); out += "Dictionary (strong)," + (afterTime-beforeTime) + "\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = array[0]; resInt = array[1]; resInt = array[2]; resInt = array[3]; resInt = array[4]; resInt = array[5]; resInt = array[6]; resInt = array[7]; resInt = array[8]; resInt = array[9]; resInt = array[10]; resInt = array[11]; resInt = array[12]; resInt = array[13]; resInt = array[14]; resInt = array[15]; resInt = array[16]; resInt = array[17]; resInt = array[18]; resInt = array[19]; } afterTime = getTimer(); out += "Array," + (afterTime-beforeTime) + "\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = vector[0]; resInt = vector[1]; resInt = vector[2]; resInt = vector[3]; resInt = vector[4]; resInt = vector[5]; resInt = vector[6]; resInt = vector[7]; resInt = vector[8]; resInt = vector[9]; resInt = vector[10]; resInt = vector[11]; resInt = vector[12]; resInt = vector[13]; resInt = vector[14]; resInt = vector[15]; resInt = vector[16]; resInt = vector[17]; resInt = vector[18]; resInt = vector[19]; } afterTime = getTimer(); out += "Vector," + (afterTime-beforeTime) + "\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = vectorFixed[0]; resInt = vectorFixed[1]; resInt = vectorFixed[2]; resInt = vectorFixed[3]; resInt = vectorFixed[4]; resInt = vectorFixed[5]; resInt = vectorFixed[6]; resInt = vectorFixed[7]; resInt = vectorFixed[8]; resInt = vectorFixed[9]; resInt = vectorFixed[10]; resInt = vectorFixed[11]; resInt = vectorFixed[12]; resInt = vectorFixed[13]; resInt = vectorFixed[14]; resInt = vectorFixed[15]; resInt = vectorFixed[16]; resInt = vectorFixed[17]; resInt = vectorFixed[18]; resInt = vectorFixed[19]; } afterTime = getTimer(); out += "Vector (fixed)," + (afterTime-beforeTime) + "\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = ifElseTree(0); resInt = ifElseTree(1); resInt = ifElseTree(2); resInt = ifElseTree(3); resInt = ifElseTree(4); resInt = ifElseTree(5); resInt = ifElseTree(6); resInt = ifElseTree(7); resInt = ifElseTree(8); resInt = ifElseTree(9); resInt = ifElseTree(10); resInt = ifElseTree(11); resInt = ifElseTree(12); resInt = ifElseTree(13); resInt = ifElseTree(14); resInt = ifElseTree(15); resInt = ifElseTree(16); resInt = ifElseTree(17); resInt = ifElseTree(18); resInt = ifElseTree(19); } afterTime = getTimer(); out += "If-Else Tree," + (afterTime-beforeTime) + "\n"; beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { resInt = doSwitch(0); resInt = doSwitch(1); resInt = doSwitch(2); resInt = doSwitch(3); resInt = doSwitch(4); resInt = doSwitch(5); resInt = doSwitch(6); resInt = doSwitch(7); resInt = doSwitch(8); resInt = doSwitch(9); resInt = doSwitch(10); resInt = doSwitch(11); resInt = doSwitch(12); resInt = doSwitch(13); resInt = doSwitch(14); resInt = doSwitch(15); resInt = doSwitch(16); resInt = doSwitch(17); resInt = doSwitch(18); resInt = doSwitch(19); } afterTime = getTimer(); out += "Switch," + (afterTime-beforeTime) + "\n"; var tf:TextField = new TextField(); tf.width = stage.stageWidth; tf.height = stage.stageHeight; tf.text = out; addChild(tf); } [Inline] static private function ifElseTree(key:int): int { if (key < 10) { if (key < 5) { if (key < 2) { if (key == 0) { return 0; } else { return 1; } } else { if (key == 2) { return 2; } else if (key == 3) { return 3; } else { return 4; } } } else { if (key < 7) { if (key == 5) { return 5; } else { return 6; } } else { if (key == 7) { return 7; } else if (key == 8) { return 8; } else { return 9; } } } } else { if (key < 15) { if (key < 12) { if (key == 10) { return 10; } else { return 11; } } else { if (key == 12) { return 12; } else if (key == 13) { return 13; } else { return 14; } } } else { if (key < 17) { if (key == 15) { return 15; } else { return 16; } } else { if (key == 17) { return 17; } else if (key == 18) { return 18; } else { return 19; } } } } return -1; } [Inline] static private function doSwitch(key:int): int { switch (key) { case 0: return 0; case 1: return 1; case 2: return 2; case 3: return 3; case 4: return 4; case 5: return 5; case 6: return 6; case 7: return 7; case 8: return 8; case 9: return 9; case 10: return 10; case 11: return 11; case 12: return 12; case 13: return 13; case 14: return 14; case 15: return 15; case 16: return 16; case 17: return 17; case 18: return 18; case 19: return 19; } return -1; } } }
I ran this test in the following environment:
- Release version of Flash Player 11.7.700.169
- 2.3 Ghz Intel Core i7
- Mac OS X 10.8.3
- ASC 2.0 build 352231 (
-debug=false -verbose-stacktraces=false -inline)
And here are the results I got:
| Map | Time |
|---|---|
| Object | 1522 |
| Dictionary (weak) | 563 |
| Dictionary (strong) | 561 |
| Array | 76 |
| Vector | 16 |
| Vector (fixed) | 16 |
| If-Else Tree | 45 |
| Switch | 56 |


The unexpected result here is that Array is actually performing worse than if-else trees despite using it with densely-packed sequential integer keys starting at zero. That’s a lot of restrictions only to take about 70% longer than the if-else trees approach.
Vector, on the other hand, fulfills its purpose as the “faster Array” class and runs about 3x faster than the second-fastest if-else trees. So, if you can handle all of the restrictions that it requires then you should definitely go with a Vector. If you need something more flexible, if-else trees still reign supreme.
Spot a bug? Have a question or suggestion? Post a comment!
#1 by Ben on April 29th, 2013
On my system Array is faster than the If-Else Tree:
Release player version: 11,7,700,169
OS: Windows 7 64 Bit
CPU: i7-2600 @ 3.4 GHz
Compiled with the AIR 3.6 SDK.
Map,Time
Object,1140
Dictionary (weak),382
Dictionary (strong),379
Array,44
Vector,14
Vector (fixed),15
If-Else Tree,106
Switch,156
#2 by jackson on April 29th, 2013
Did you use ASC 2.0 with the same compile flags that I did? If not, the
if-elsetree andswitchtests will run much slower due to the lack of function inlining via[Inline].#3 by Ben on June 3rd, 2013
Sorry that it took a while. I retested it and with the -inline option I had the following results:
OS: Windows 7 64 Bit
CPU: i7-2600 @ 3.4 GHz
Compiled with the AIR 3.7 SDK.
Map,Time
Object,1125
Dictionary (weak),375
Dictionary (strong),390
Array,46
Vector,16
Vector (fixed),16
If-Else Tree,46
Switch,47
The results match your results in my opinion.
#4 by henke37 on April 29th, 2013
You forgot ByteArray with a manually computed position.
#5 by jackson on April 29th, 2013
I’m not sure I know what you mean. Do you mean that the key should be transformed into a position index and then some value read at that location? Like this:
#6 by ben w on April 29th, 2013
Could you include a standard if else in there too, and as you are using ASC2.0 makes sense to try out the fast mem op codes, in my tests they allow for faster access that Vector. Not as practical but would probably be the fastest contender.
#7 by jackson on April 29th, 2013
I could include
if-elseladders, but I disqualified them based on its poor showing in ASC 2.0 Conditionals Performance.As for the domain memory (a.k.a. “Alchemy”, “fast memory”) opcodes support in ASC 2.0, that is a good idea. I may combine it with Ben’s comment above. I’ve also got some more general articles on the subject in the works, but I’d love to hear any specific ideas you (or anybody reading this) may have for articles on domain memory opcode support in ASC 2.0.
#8 by Glen Blanchard on April 29th, 2013
Good find, always love to see these types of tests.
Using integer indexes for the Object test will improve it’s speed and make it comparable to Dictionary in my tests though still way slower than the other faster tests.