Today’s article is about three totally unrelated discoveries I’ve recently made in AS3. These answer three questions I’ve recently had. Should you cache the object you’re looping over with a for-each loop as a local variable? Can you clear a Dictionary or Object with a for-in loop? Is it faster to write your own version of Vector.indexOf? All of these questions are answered in today’s article!

First, should you cache the object you’re looping over with a for-each loop as a local variable? Here’s the suspect code:

for each (var val:SomeType in some.very.long.nested.deep.expression)
{
}

Often, some.very.long.nested.deep.expression may be very expensive to retrieve. For example, some of those innocuous-looking fields may in fact be getter functions (function get x(): int) and therefore very costly to call if the expression is evaluated every iteration of the loop. That’s the key: is it evaluated every iteration of the loop? Here’s some code to test it out:

package
{
	import flash.display.*;
	import flash.text.*;
	import flash.utils.*;
 
	public class ForEachTest extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function row(...cols): void
		{
			__logger.appendText(cols.join(",")+"\n");
		}
 
		public function ForEachTest()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			var beforeTime:int;
			var afterTime:int;
			var i:int;
			var REPS:int = 10;
			var SIZE:int = 10000000;
			var val:int;
			var vec:Vector.<int> = new Vector.<int>(SIZE);
			for (i = 0; i < SIZE; ++i)
			{
				vec[i] = i;
			}
			var obj:Object = { the: { vector: { val: vec } } };
			var count:int;
 
			count = 0;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				for each (val in vec)
				{
					count += val;
				}
			}
			afterTime = getTimer();
			row("Direct,", (afterTime-beforeTime));
 
			count = 0;
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				for each (val in obj.the.vector.val)
				{
					count += val;
				}
			}
			afterTime = getTimer();
			row("Indirect,", (afterTime-beforeTime));
		}
	}
}

I ran this test in the following environment:

  • Flex SDK (MXMLC) 4.6.0.23201, compiling in release mode (no debugging or verbose stack traces)
  • Release version of Flash Player 11.5.31.5
  • 2.3 Ghz Intel Core i7
  • Mac OS X 10.8.2

And here are the results I got:

Method Time
Direct 2428
Indirect 2428

At 100 million iterations, I’d say it’s pretty clear that there’s no need to worry about having a bit expression at the end of your for-each loop.

Next, can you clear a Dictionary or Object with a for-in loop? Here’s a test app:

package
{
	import flash.display.*;
	import flash.text.*;
	import flash.utils.*;
 
	public class DeleteDictionaryTest extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function row(...cols): void
		{
			__logger.appendText(cols.join(",")+"\n");
		}
 
		public function DeleteDictionaryTest()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			var obj:Object = { first:"Jackson", last:"Dunstan" };
			var key:String;
			for (key in obj)
			{
				delete obj[key];
			}
			row("begin obj properties...");
			for (key in obj)
			{
				row("\tobj[" + key + "] = " + obj[key]);
			}
			row("...end obj properties");
		}
}

And here’s what it prints:

begin obj properties...
...end obj properties

So yes, you definitely can delete the keys of a Dictionary while you loop over it with a for-in loop.

Finally, is it faster to write your own version of Vector.indexOf? Let’s look at the test app:

package
{
	import flash.display.*;
	import flash.text.*;
	import flash.utils.*;
 
	public class Playground extends Sprite
	{
		private var __logger:TextField = new TextField();
		private function row(...cols): void
		{
			__logger.appendText(cols.join(",")+"\n");
		}
 
		public function Playground()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			__logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(__logger);
 
			var beforeTime:int;
			var afterTime:int;
			var i:int;
			var SIZE:int = 10000000;
			var vec:Vector.<int> = new Vector.<int>(SIZE);
			for (i = 0; i < SIZE; ++i)
			{
				vec[i] = i;
			}
			var search:int;
			var index:int;
 
			search = 0;
			index = -1;
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				if (vec[i] == search)
				{
					index = i;
					break;
				}
			}
			afterTime = getTimer();
			row("Manual (first),", (afterTime-beforeTime));
 
			search = SIZE/2;
			index = -1;
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				if (vec[i] == search)
				{
					index = i;
					break;
				}
			}
			afterTime = getTimer();
			row("Manual (half-way),", (afterTime-beforeTime));
 
			search = SIZE;
			index = -1;
			beforeTime = getTimer();
			for (i = 0; i < SIZE; ++i)
			{
				if (vec[i] == search)
				{
					index = i;
					break;
				}
			}
			afterTime = getTimer();
			row("Manual (end),", (afterTime-beforeTime));
 
			search = 0;
			index = vec.indexOf(search);
			afterTime = getTimer();
			row("indexOf (first),", (afterTime-beforeTime));
 
			search = SIZE/2;
			index = vec.indexOf(search);
			afterTime = getTimer();
			row("indexOf (half-way),", (afterTime-beforeTime));
 
			search = SIZE;
			index = vec.indexOf(search);
			afterTime = getTimer();
			row("indexOf (end),", (afterTime-beforeTime));
		}
	}
}

I ran this in the same environment as above and got these results:

Method Search for first Search for middle Search for last
Manual 0 13 31
Vector.indexOf 0 13 24

These results are on a 10 million long Vector and are basically the same all the way up to when the element you’re looking for is at 5 million. Then, at least on my test computer, the Vector.indexOf function starts to outpace the for loop to the point that the for loop is about 30% slower at the 10 millionth element. So, if you have a very long Vector you should use indexOf. Otherwise, it’s a bit of a toss-up. You might see some performance gains on a lot of searches that don’t go very far into the Vector because you’re eliminating the function call overhead of indexOf.

Spot a bug? Have a discovery to share or a question to ask? Post a comment!