Array vs. Vector Part II
Today’s article is in response to some interesting comments on the previous article comparing Array‘s performance to that of Vector. Today I’ll test different types of Vectors and the performance of deleting elements.
Intro
Jean-Phillipe Auclair commented that deleting elements was his problem with Array and Vector. Meanwhile, aaulia commented that the pain was in storing Objects in Arrays and Vectors. So I decided to test both of these to assess the performance and hopefully turn up a winner in these scenarios. Firstly, I decided to test deletion by traversing from back-to-front so as to not trigger any splits between Array‘s dense portion and hash table portion, which would be an unfair disadvantage when compared to Vector. Secondly, I decided to test a number of built-in types of Vectors: int, uint, Number, Boolean, String, and an empty custom type MyClass. This means that I more-or-less match the comparion aaulia linked to. While there are more built-in types, I think this is a pretty representative set for my testing purposes. So, let’s have a look at the test app:
Test App
package { import flash.display.*; import flash.text.*; import flash.utils.*; public class ArrayVsVector extends Sprite { public function ArrayVsVector() { var logger:TextField = new TextField(); logger.autoSize = TextFieldAutoSize.LEFT; addChild(logger); function log(msg:*): void { logger.appendText(msg+"\n"); } const REPS:int = 50; const SIZE:int = 100000; const VAL_INT:int = 42; const VAL_UINT:uint = 42; const VAL_NUMBER:Number = 42.0; const VAL_BOOLEAN:Boolean = true; const VAL_STRING:String = "42"; const VAL_OBJECT:MyClass = new MyClass(); var i:int; var j:int; var temp:*; var beforeTime:int; var afterTime:int; var array:Array = new Array(SIZE); var vectorInt:Vector.<int> = new Vector.<int>(SIZE, false); var vectorUInt:Vector.<uint> = new Vector.<uint>(SIZE, false); var vectorNumber:Vector.<Number> = new Vector.<Number>(SIZE, false); var vectorBoolean:Vector.<Boolean> = new Vector.<Boolean>(SIZE, false); var vectorString:Vector.<String> = new Vector.<String>(SIZE, false); var vectorObject:Vector.<MyClass> = new Vector.<MyClass>(SIZE, false); for (i = 0; i < SIZE; ++i) { array[i] = VAL_INT; vectorInt[i] = VAL_INT; vectorUInt[i] = VAL_UINT; vectorNumber[i] = VAL_NUMBER; vectorBoolean[i] = VAL_BOOLEAN; vectorString[i] = VAL_STRING; vectorObject[i] = VAL_OBJECT; } log("Read:"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { array[j]; } } afterTime = getTimer(); log("\tArray: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorInt[j]; } } afterTime = getTimer(); log("\tVector (int): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorUInt[j]; } } afterTime = getTimer(); log("\tVector (uint): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorNumber[j]; } } afterTime = getTimer(); log("\tVector (Number): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorBoolean[j]; } } afterTime = getTimer(); log("\tVector (Boolean): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorString[j]; } } afterTime = getTimer(); log("\tVector (String): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorObject[j]; } } afterTime = getTimer(); log("\tVector (Object): " + (afterTime-beforeTime)); log("Write:"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { array[j] = VAL_INT; } } afterTime = getTimer(); log("\tArray: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorInt[j] = VAL_INT; } } afterTime = getTimer(); log("\tVector (int): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorUInt[j] = VAL_UINT; } } afterTime = getTimer(); log("\tVector (uint): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorNumber[j] = VAL_NUMBER; } } afterTime = getTimer(); log("\tVector (Number): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorBoolean[j] = VAL_BOOLEAN; } } afterTime = getTimer(); log("\tVector (Boolean): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorString[j] = VAL_STRING; } } afterTime = getTimer(); log("\tVector (String): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = 0; j < SIZE; ++j) { vectorObject[j] = VAL_OBJECT; } } afterTime = getTimer(); log("\tVector (Object): " + (afterTime-beforeTime)); log("Delete:"); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = SIZE-1; j >= 0; --j) { delete array[j]; } } afterTime = getTimer(); log("\tArray: " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = SIZE-1; j >= 0; --j) { delete vectorInt[j]; } } afterTime = getTimer(); log("\tVector (int): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = SIZE-1; j >= 0; --j) { delete vectorUInt[j]; } } afterTime = getTimer(); log("\tVector (uint): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = SIZE-1; j >= 0; --j) { delete vectorNumber[j]; } } afterTime = getTimer(); log("\tVector (Number): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = SIZE-1; j >= 0; --j) { delete vectorBoolean[j]; } } afterTime = getTimer(); log("\tVector (Boolean): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = SIZE-1; j >= 0; --j) { delete vectorString[j]; } } afterTime = getTimer(); log("\tVector (String): " + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { for (j = SIZE-1; j >= 0; --j) { delete vectorObject[j]; } } afterTime = getTimer(); log("\tVector (Object): " + (afterTime-beforeTime)); } } } class MyClass {}
Read Performance
Environment | Array | Vector (int) | Vector (uint) | Vector (Number) | Vector (Boolean) | Vector (String) | Vector (Object) |
---|---|---|---|---|---|---|---|
3.0 Ghz Intel Core 2 Duo, Windows XP | 38 | 29 (31%) | 31 (23%) | 30 (27%) | 29 (31%) | 29 (31%) | 30 (27%) |
2.0 Ghz Intel Core 2 Duo, Mac OS X 10.5 | 69 | 53 (30%) | 53 (30%) | 54 (28%) | 51 (35%) | 51 (35%) | 51 (35%) |
Write Performance
Environment | Array | Vector (int) | Vector (uint) | Vector (Number) | Vector (Boolean) | Vector (String) | Vector (Object) |
---|---|---|---|---|---|---|---|
3.0 Ghz Intel Core 2 Duo, Windows XP | 96 | 37 (259%) | 37 (259%) | 39 (246%) | 72 (33%) | 90 (7%) | 131 (-36%) |
2.0 Ghz Intel Core 2 Duo, Mac OS X 10.5 | 162 | 64 (253%) | 63 (257%) | 55 (295%) | 203 (-25%) | 253 (-56%) | 231 (-43%) |
Delete Performance
Environment | Array | Vector (int) | Vector (uint) | Vector (Number) | Vector (Boolean) | Vector (String) | Vector (Object) |
---|---|---|---|---|---|---|---|
3.0 Ghz Intel Core 2 Duo, Windows XP | 4544 | 4429 (3%) | 4431 (3%) | 4473 (2%) | 4506 (1%) | 4510 (1%) | 4485 (1%) |
2.0 Ghz Intel Core 2 Duo, Mac OS X 10.5 | 4989 | 4844 (3%) | 4842 (3%) | 4841 (3%) | 4897 (2%) | 4902 (2%) | 5159 (-3%) |
Performance Analysis
Jean-Phillipe was definitely correct about the cost of deletion. Deletion seems to be about 20 times slower than reading or writing, so you should avoid it if you can. For example, it’d be a lot cheaper to write a NaN or some other special value (null, -1, etc.) than to actually remove the element of the Array or Vector. Given the span from -1 to 3% though, it seems to make about no difference which container you delete your data from.
aaulia was also correct about writing Vectors of Objects. While reading them makes little difference compared to other types of data, writing them is about five times slower than writing other types and actually manages to make Vector slower than Array! This is sad, but it’s always good to know where you stand performance-wise when considering optimizing a chunk of code for performance.
Simply switching from Array to Vector will not always increase performance. In the case of deletion, you will not see any significant performance improvement and may see a slight performance drop on the Mac. In the case of heavy writes of Objects to the collection, switching will hurt your performance by 30-50%. While it would be very nice to flatly state “Vectors are faster than Arrays“, it seems as though that would not always be correct. We’ve just added two more reasons why that can be incorrect to the one we already knew about.
#1 by TwoFace on March 23rd, 2010 ·
Hm, just wondering, what will be with object when you will not delete it from array/vector but replace it with NaN/null? Does the GC collect it or what?
#2 by aaulia on March 23rd, 2010 ·
If your reference to the object is only the array/vector, then yes it will be GC-ed. But if you have reference to the object other than the one in the array/vector (another variable maybe) then the object will live on, until the reference is removed. The best solution for things like this is Object Polling, you don’t have to delete the object, instead you recycle it.
#3 by Miracle on January 12th, 2012 ·
Your posting really straihgetend me out. Thanks!
#4 by jpauclair on March 23rd, 2010 ·
You delete multiple time the same element.
Also, is “delete” actualy removing the element? I guess not if you can loop on it again…
#5 by jackson on March 23rd, 2010 ·
You’re right that delete is doing nothing to the Vector and setting the element to undefined in the Array. In the Array‘s case, it may also break the dense/hash portions up, but that’s not the case here given the deletion at the end.
Given that, it seems very odd that delete would take so long. Were you perhaps referring to the time taken by shift, pop, and splice instead?
#6 by jpauclair on March 23rd, 2010 ·
delete the element from a vector is doing nothing exept returning true, the reference stay the same.
For the array, it sets the element to undefined.
#7 by Joist on October 24th, 2011 ·
I’m curious to know why you use delete on the vectorObjects instead of nulling the reference.
#8 by jackson on October 24th, 2011 ·
That’s definitely the faster way of going, but in this case the point of the test was to compare the performance of
delete
onArray
andVector
in case you ever do want to do it. Hopefully the slow results convince you not to. :)#9 by Ben on June 3rd, 2013 ·
I know this is a very old post, but I was interested if the result would be similar in the current version of Flash Player. It seems with the new version the Vector got faster. Here are my results:
WIN 11,7,700,202 Desktop (Windows 7 64)
Intel i7-2600 @ 3.4 GHz
Read:
Array: 16
Vector (int): 0
Vector (uint): 15
Vector (Number): 16
Vector (Boolean): 0
Vector (String): 15
Vector (Object): 0
Write:
Array: 63
Vector (int): 15
Vector (uint): 16
Vector (Number): 16
Vector (Boolean): 46
Vector (String): 32
Vector (Object): 31
Delete:
Array: 2139
Vector (int): 890
Vector (uint): 889
Vector (Number): 889
Vector (Boolean): 968
Vector (String): 941
Vector (Object): 905
It’s strange that there are some 0 values. The 0 values sometimes jump to Array or to other Vector tests. So I’m a bit skeptic about the results of the read category.
#10 by jackson on June 3rd, 2013 ·
Compared to the results from the article, deleting and writing vectors seems much faster in your results. It looks like three years of hardware improvement has made the read test insufficiently fast. If you want to get more accurate numbers there, try increasing the
REPS
.#11 by Ben on June 3rd, 2013 ·
Good point. I increased REPS to 400 and got the following results:
Read:
Array: 95
Vector (int): 77
Vector (uint): 79
Vector (Number): 76
Vector (Boolean): 81
Vector (String): 80
Vector (Object): 78
Write:
Array: 517
Vector (int): 109
Vector (uint): 110
Vector (Number): 121
Vector (Boolean): 340
Vector (String): 275
Vector (Object): 282
Delete:
Array: 18547
Vector (int): 7636
Vector (uint): 7697
Vector (Number): 7489
Vector (Boolean): 7681
Vector (String): 7896
Vector (Object): 7818
Seems that Array is the slower in every case.
#12 by jackson on June 3rd, 2013 ·
Now your read results look like those in the article: about 20% faster. If anything,
Vector
is slower in your test since the article’s results have it about 20-30% faster. This could be due to Flash Player, OS, browser, or hardware differences.#13 by Ben on June 4th, 2013 ·
I agree, but the write and delete performance of the Array seems to be far worse compared to your results. But it may also be caused by OS, browser, or hardware differences.
#14 by Peter on July 22nd, 2015 ·
An update :-)
FP 18
Intel Xeon 3.4Ghz, Win7 64bit
FlashDevelop 5.01
200 REPS
Release build
Read:
Array: 130
Vector (int): 70
Vector (uint): 70
Vector (Number): 60
Vector (Boolean): 70
Vector (String): 70
Vector (Object): 70
Write:
Array: 260
Vector (int): 90
Vector (uint): 80
Vector (Number): 80
Vector (Boolean): 190
Vector (String): 170
Vector (Object): 180
Delete:
Array: 5480
Vector (int): 3220
Vector (uint): 3220
Vector (Number): 3250
Vector (Boolean): 3290
Vector (String): 3330
Vector (Object): 3291
I expected a difference between debug and release mode, but not this much. Always remember to release build ;-)
Debug build
Read:
Array: 1140
Vector (int): 1070
Vector (uint): 1070
Vector (Number): 1070
Vector (Boolean): 1060
Vector (String): 1070
Vector (Object): 1070
Write:
Array: 1260
Vector (int): 1080
Vector (uint): 1090
Vector (Number): 1080
Vector (Boolean): 1161
Vector (String): 1170
Vector (Object): 1180
Delete:
Array: 6940
Vector (int): 4434
Vector (uint): 4460
Vector (Number): 4410
Vector (Boolean): 4521
Vector (String): 4561
Vector (Object): 4470