XML E4X Operators vs. XML Class Methods
We know that the XML E4X operators are an 10x slower than plain Object
, but how slow are they compared to the XML
class’ methods like elements()
and attributes
? Today’s article finds that out.
The following test app is the same as with the last article in this series, but with two important changes. First, the E4X descendant operator test has been implemented correctly this time around. Second, and more on-topic to this article, the same XML
object has been accessed in two ways. The first is the same as last time and employes the E4X operators. The second is by using the XML
class’ methods: elements()
, attributes()
, and descendants()
.
package { import flash.display.*; import flash.utils.*; import flash.text.*; public class XMLOperatorVsMethod 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 XMLOperatorVsMethod() { 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 = 1000000; 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 (E4X)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.@c; } afterTime = getTimer(); row("XML Attribute Does Not Exist (E4X)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.d; } afterTime = getTimer(); row("XML Element Exists (dot) (E4X)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.e; } afterTime = getTimer(); row("XML Element Does Not Exist (dot) (E4X)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ..f; } afterTime = getTimer(); row("XML Descendent Exists (E4X)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ..g; } afterTime = getTimer(); row("XML Descendent Does Not Exist (E4X)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.attribute("b"); } afterTime = getTimer(); row("XML Attribute Exists (Method)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.attribute("c"); } afterTime = getTimer(); row("XML Attribute Does Not Exist (Method)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.elements("d"); } afterTime = getTimer(); row("XML Element Exists (dot) (Method)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.elements("e"); } afterTime = getTimer(); row("XML Element Does Not Exist (dot) (Method)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.descendants("f"); } afterTime = getTimer(); row("XML Descendent Exists (Method)", (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < REPS; ++i) { XML_OBJ.descendants("g"); } afterTime = getTimer(); row("XML Descendent Does Not Exist (Method)", (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) { 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"; }
I ran this test in the following environment:
- Release version of Flash Player 12.0.0.41
- 2.3 Ghz Intel Core i7-3615QM
- Mac OS X 10.9.1
- Google Chrome 32.0.1700.102
- 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 (E4X) | 344 |
XML Attribute Does Not Exist (E4X) | 304 |
XML Element Exists (dot) (E4X) | 284 |
XML Element Does Not Exist (dot) (E4X) | 286 |
XML Descendent Exists (E4X) | 257 |
XML Descendent Does Not Exist (E4X) | 262 |
XML Attribute Exists (Method) | 688 |
XML Attribute Does Not Exist (Method) | 668 |
XML Element Exists (dot) (Method) | 232 |
XML Element Does Not Exist (dot) (Method) | 239 |
XML Descendent Exists (Method) | 262 |
XML Descendent Does Not Exist (Method) | 266 |
XML Element Exists (bracket) | 330 |
XML Element Does Not Exist (bracket) | 335 |
Plain Object Exists | 31 |
Plain Object Does Not Exist | 53 |
Typed Object Exists | 2 |
Typed Object Does Not Exist | 0 |
The second graph makes the comparison very clear. XML
class methods take about twice as long as E4X operators when accessing attributes. For elements, however, they are slightly faster. Descendants are a dead heat.
Even with the minor speed boost when accessing elements, the XML
class methods are still an order of magnitude slower than plain objects: the Object
class. Typed class
instances are an order of magnitude quicker than that. So the advice from the last article still stands: do not use XML
objects in performance critical code. If you need to repeatedly access an XML document, parse it into typed class
instances and access those.
Spot a bug? Have a question or suggestion? Post a comment!
#1 by Islam on February 3rd, 2014 ·
hey Jackson,
it looks like you have a mistake in the code in the lines before calling init() method because you shouldn’t reassign XML_OBJ and PLAIN_OBJ objects again.
#2 by jackson on February 3rd, 2014 ·
Good catch. I’ve re-run the test without this error and the results are more-or-less the same as with it.
#3 by Aurelain on February 10th, 2014 ·
Hi, Jackson.
Did you by any chance also investigate the XMLDocument (legacy) class?
http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/xml/XMLDocument.html
I remember testing it some 2 years ago and it was blazing fast compared to the E4X method.
The only thing that kept undermining my decision to use it was the “legacy” word :).
#4 by jackson on February 10th, 2014 ·
I didn’t, but phrases like “blazing fast” pique my interest. :)
#5 by jackson on February 14th, 2014 ·
Now I have. This is the subject of Monday’s article. Thanks for the tip!