I recently received an e-mail from asking which is faster: a `DisplayObjectContainer` or a `Vector` of `DisplayObject`. To ask this is to question whether or not we can do better than the Flash Player’s native container of `DisplayObjects` using AS3. It turns out that we can. Read on for several ways to improve on `DisplayObjectContainer`‘s speed.

For today’s comparison test I’ll be taking the above challenge but adding, out of curiosity, a few more collections. Here’s the complete list of what I’m testing and why:

• `Sprite`– the simplest form of `DisplayObjectContainer`
• `Vector`– the fastest indexed collection as of Flash Player 10.1
• `Array`– in case Flash Player 9 support is required
• `MovieClip`– in case we’re dealing with animation or an FLA
• `QuickSpriteVector`– a `Sprite` derivative that holds a `Vector` of its children
• `QuickSpriteArray`– a `Sprite` derivative that holds an `Array` of its children, in case of Flash Player 9

We should compare them in multiple ways so as to get a full picture of the performance characteristics in a variety of use cases. The following test will use three approaches:

• Index– get a `DisplayObject` by index
• Search (best case)– linear search for a `DisplayObject` that happens to be the first in the list
• Search (worst base)– linear search for a `DisplayObject` that happens to not be in the list

Let’s take a look at the performance test:

```package
{
import flash.display.*;
import flash.utils.*;
import flash.text.*;

public class HoldingDisplayObjects extends Sprite
{
private var __logger:TextField = new TextField();
private function log(msg:*): void { __logger.appendText(msg + "\n"); }

public function HoldingDisplayObjects()
{
__logger.autoSize = TextFieldAutoSize.LEFT;

const SIZE:int = 100;
const REPS:int = 1000000;
const INDEX:int = SIZE / 2;
var i:int;
var j:int;
var beforeTime:int;
var afterTime:int;
var childFirstArr:DisplayObject;
var childFirstVec:DisplayObject;
var childFirstSpr:DisplayObject;
var childFirstMov:DisplayObject;
var childFirstQSA:DisplayObject;
var childFirstQSV:DisplayObject;
var childNotInList:DisplayObject;
var arr:Array = [];
var vec:Vector.<DisplayObject> = new Vector.<DisplayObject>();
var spr:Sprite = new Sprite();
var mov:MovieClip = new MovieClip();
var qsa:QuickSpriteArray = new QuickSpriteArray();
var qsv:QuickSpriteVector = new QuickSpriteVector();
var qsaChildren:Array = qsa.children;
var qsvChildren:Vector.<DisplayObject> = qsv.children;

for (i = 0; i < SIZE; ++i)
{
arr.push(new Sprite());
vec.push(new Sprite());
}
childFirstArr = arr;
childFirstVec = vec;
childFirstSpr = spr.getChildAt(0);
childFirstMov = mov.getChildAt(0);
childFirstQSA = qsaChildren;
childFirstQSV = qsvChildren;

log("Index");

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
arr[INDEX];
}
afterTime = getTimer();
log("\tArray: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
vec[INDEX];
}
afterTime = getTimer();
log("\tVector: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
spr.getChildAt(INDEX);
}
afterTime = getTimer();
log("\tSprite: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
mov.getChildAt(INDEX);
}
afterTime = getTimer();
log("\tMovieClip: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
qsaChildren[INDEX];
}
afterTime = getTimer();
log("\tQuickSpriteArray: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
qsvChildren[INDEX];
}
afterTime = getTimer();
log("\tQuickSpriteVector: " + (afterTime-beforeTime));

log("Search (best case)");

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (arr[j] == childFirstArr)
{
break;
}
}
}
afterTime = getTimer();
log("\tArray: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (vec[j] == childFirstVec)
{
break;
}
}
}
afterTime = getTimer();
log("\tVector: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (spr.getChildAt(j) == childFirstSpr)
{
break;
}
}
}
afterTime = getTimer();
log("\tSprite: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (mov.getChildAt(j) == childFirstMov)
{
break;
}
}
}
afterTime = getTimer();
log("\tMovieClip: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (qsaChildren[j] == childFirstQSA)
{
break;
}
}
}
afterTime = getTimer();
log("\tQuickSpriteArray: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (qsvChildren[j] == childFirstQSV)
{
break;
}
}
}
afterTime = getTimer();
log("\tQuickSpriteVector: " + (afterTime-beforeTime));

log("Search (worst case)");

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (arr[j] == childNotInList)
{
break;
}
}
}
afterTime = getTimer();
log("\tArray: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (vec[j] == childNotInList)
{
break;
}
}
}
afterTime = getTimer();
log("\tVector: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (spr.getChildAt(j) == childNotInList)
{
break;
}
}
}
afterTime = getTimer();
log("\tSprite: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (mov.getChildAt(j) == childNotInList)
{
break;
}
}
}
afterTime = getTimer();
log("\tMovieClip: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (qsaChildren[j] == childNotInList)
{
break;
}
}
}
afterTime = getTimer();
log("\tQuickSpriteArray: " + (afterTime-beforeTime));

beforeTime = getTimer();
for (i = 0; i < REPS; ++i)
{
for (j = 0; j < SIZE; ++j)
{
if (qsvChildren[j] == childNotInList)
{
break;
}
}
}
afterTime = getTimer();
log("\tQuickSpriteVector: " + (afterTime-beforeTime));
}
}
}
import flash.display.*;
class QuickSpriteVector extends Sprite
{
public var children:Vector.<DisplayObject> = new Vector.<DisplayObject>();
{
if (this.children.indexOf(child) < 0)
{
this.children.push(child);
}
return child;
}
// TODO: Implement addChildAt, removeChild, removeChildAt, setChildIndex,
//       swapChildren, and swapChildrenAt
}
class QuickSpriteArray extends Sprite
{
public var children:Array = [];
{
if (this.children.indexOf(child) < 0)
{
this.children.push(child);
}
return child;
}
// TODO: Implement addChildAt, removeChild, removeChildAt, setChildIndex,
//       swapChildren, and swapChildrenAt
}```

Most of this test is quite straightforward, but the `QuickSpriteVector` and `QuickSpriteArray` need a bit of explaining:

• Both are incomplete, as evidenced by the “TODO” comment. `addChild` is sufficient for the performance test.
• `children` is public, which allows outside objects to get and change it:
• They can break this object’s internal state
• Access (dot operator) is much faster than an interface function (e.g. `getChildren`)
• Use (index operator) is much faster than interfaces like `getChildAt` function calls

With that in mind, let’s take a look at the performance results using Flash Player 10.1 on a 2.4 Ghz Intel Core i5 with Mac OS X:

Collection Index Search (best case) Search (worst case)
Array 9 20 2270
Vector 7 20 2070
Sprite 30 31 3139
MovieClip 30 32 3096
QuickSpriteArray 9 21 2255
QuickSpriteVector 7 19 2082

From the above, we can glean a few key findings:

• `QuickSpriteVector` and `QuickSpriteArray` predictably perform as well as their `Vector` and `Array` counterparts since all operations on them are via cached local variable references to their children
• `Vector` and `Array` are about 3x faster when indexing
• Searching in the best case (first element) and worst case (element not found) is about 50% faster with `Vector` and `Array`
• `Vector` edges out `Array` slightly, as seen in many other tests on this site

So if accessing the elements of your `DisplayObjectContainers` is a cause of slowness for you, consider using an approach like `QuickSprite`. You’ll get a 3x performance boost for indexing and a 50% performance boost for searching!
{"ft_lang_label":"__label__en","ft_lang_prob":0.59802216,"math_prob":0.9317527,"size":9949,"snap":"2023-14-2023-23","text_gpt3_token_len":2801,"char_repetition_ratio":0.19939668,"word_repetition_ratio":0.3681063,"special_character_ratio":0.2939994,"punctuation_ratio":0.20160827,"nsfw_num_words":0,"has_unicode_error":false,"math_prob_llama3":0.9785133,"pos_list":[0],"im_url_duplicate_count":[null],"WARC_HEADER":"{\"WARC-Type\":\"response\",\"WARC-Date\":\"2023-03-25T11:39:55Z\",\"WARC-Record-ID\":\"<urn:uuid:940bd5b3-d2ee-4df1-b211-0f2514be123b>\",\"Content-Length\":\"67009\",\"Content-Type\":\"application/http; msgtype=response\",\"WARC-Warcinfo-ID\":\"<urn:uuid:4b584e4f-2e7e-4bcd-83cc-3efdb92c2b7f>\",\"WARC-Concurrent-To\":\"<urn:uuid:92936288-3fba-4a9f-a192-c5cef3764d01>\",\"WARC-IP-Address\":\"104.21.90.208\",\"WARC-Target-URI\":\"https://www.jacksondunstan.com/articles/876\",\"WARC-Payload-Digest\":\"sha1:MV62C6673QMPQTCY6SSEFHG2WYHS4EMJ\",\"WARC-Block-Digest\":\"sha1:62DL54K4VW2FMMYTX74VQK322NOUQZAI\",\"WARC-Identified-Payload-Type\":\"text/html\",\"warc_filename\":\"/cc_download/warc_2023/CC-MAIN-2023-14/CC-MAIN-2023-14_segments_1679296945323.37_warc_CC-MAIN-20230325095252-20230325125252-00322.warc.gz\"}"}