AS3 has always had great support for XML built right into the language, but how fast is it? Today’s article compares the performance of operators that work on XML objects like .@x, .x, ..x, and ["x"] against their equivalents in plain Object instances and typed class instances. Just how slow are the XML operators? Read on to find out.

Here is the source code for the test app. It’s a very straightforward measurement of each XML operator’s speed when attributes and elements both exist and don’t exist. Plain objects and typed (class instance) objects are also tested. The only exception is with typed objects because there is no equivalent way to test when a property doesn’t exist. This is because you’ll get a compiler error with the dot (.x) operator and a runtime error with the bracket (["x"]) operator.

package
{
	import flash.display.*;
	import flash.utils.*;
	import flash.text.*;
 
	public class XMLOperatorSpeed extends Sprite
	{
		private var logger:TextField = new TextField();
		private function row(...cols): void
		{
			logger.appendText(cols.join(",") + "\n");
		}
 
		private static var XML_OBJ:XML = <a b="x"><d><f/></d></a>;
		private static var PLAIN_OBJ:Object = { b:"x" };
		private static var TYPED_OBJ:TypedObj;
 
		public function XMLOperatorSpeed()
		{
			stage.align = StageAlign.TOP_LEFT;
			stage.scaleMode = StageScaleMode.NO_SCALE;
 
			logger.autoSize = TextFieldAutoSize.LEFT;
			addChild(logger);
 
			XML_OBJ = <a b="x"/>;
			PLAIN_OBJ = { b:"x" };
			TYPED_OBJ = new TypedObj();
 
			init();
		}
 
		private function init(): void
		{
			const REPS:int = 10000000;
			var i:int;
			var beforeTime:int;
			var afterTime:int;
 
			row("Operation", "Time");
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ.@b;
			}
			afterTime = getTimer();
			row("XML Attribute Exists", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ.@c;
			}
			afterTime = getTimer();
			row("XML Attribute Does Not Exist", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ.d;
			}
			afterTime = getTimer();
			row("XML Element Exists (dot)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ.e;
			}
			afterTime = getTimer();
			row("XML Element Does Not Exist (dot)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ["d"];
			}
			afterTime = getTimer();
			row("XML Element Exists (bracket)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ["e"];
			}
			afterTime = getTimer();
			row("XML Element Does Not Exist (bracket)", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ.f;
			}
			afterTime = getTimer();
			row("XML Descendent Exists", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				XML_OBJ.g;
			}
			afterTime = getTimer();
			row("XML Descendent Does Not Exist", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				PLAIN_OBJ.b;
			}
			afterTime = getTimer();
			row("Plain Object Exists", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				PLAIN_OBJ.c;
			}
			afterTime = getTimer();
			row("Plain Object Does Not Exist", (afterTime-beforeTime));
 
			beforeTime = getTimer();
			for (i = 0; i < REPS; ++i)
			{
				TYPED_OBJ.b;
			}
			afterTime = getTimer();
			row("Typed Object Exists", (afterTime-beforeTime));
 
			row("Typed Object Does Not Exist", 0);
		}
	}
}
class TypedObj
{
	public var b:String = "x";
}

Run the test

I ran this test in the following environment:

  • Release version of Flash Player 11.9.900.170
  • 2.3 Ghz Intel Core i7
  • Mac OS X 10.9.1
  • Google Chrome 31.0.1650.63
  • ASC 2.0.0 build 354071 (-debug=false -verbose-stacktraces=false -inline -optimize=true)

And here are the results I got:

Operation Time
XML Attribute Exists 3328
XML Attribute Does Not Exist 2913
XML Element Exists (dot) 2704
XML Element Does Not Exist (dot) 2724
XML Element Exists (bracket) 3287
XML Element Does Not Exist (bracket) 3164
XML Descendent Exists 2719
XML Descendent Does Not Exist 2716
Plain Object Exists 306
Plain Object Does Not Exist 477
Typed Object Exists 18
Typed Object Does Not Exist 0

Results Graph

With the exception of XML attributes, it doesn’t seem to make much of a difference whether the item you’re looking for exists or not. Plain objects, however, take about 50% more time when an item doesn’t exist. As mentioned earlier, typed objects don’t have equivalent functionality.

Overall, XML operators are about 10x slower than plain object operators. This will vary depending on the complexity of your XML document as more and more attributes and elements need to be searched, especially by the descendent (..x) operator. The above test uses the smallest possible XML object to carry out the tests, so think of these as best-case times. Of course there is no descendent operator for plain objects, so you’d need to recursively search for its children which would also take more time.

Typed objects clearly beat both XML and plain objects: they’re about 17x faster. However, they’re far less flexible than their competitors in this area. I’ve included them mostly as a reference point to see how much slower than “normal” class-based code these plain objects and XML objects are. In short, plain objects are an order of magnitude slower than typed objects and XML objects are yet another order of magnitude slower than plain objects. Do not use them in performance-critical code.

I suppose that no article about XML would be complete without mentioning JSON. Flash’s JSON.parse function returns a plain object and provides you with no special operators as there are with XML. So while you lose some operator convenience, you’ll get about 10x faster access to the loaded JSON data.

Spot a bug? Have a question or suggestion? Post a comment!