XML Speed
XML is widely used in AS3 applications for everything from simple configuration files to complex networking protocols. AS3 even includes 10 operators in its syntax specifically to make XML easier to work with. This often leads to AS3 developers loading XML documents and then just leaving them as an XML
objects. XML’s performance begins to seep into the rest of the AS3 application. Today we look at just how much this can slow down our apps.
XML is usually loaded from a text file with a class like URLLoader
and constructed by passing the loaded String
to the XML
constructor. However, if you need to build one yourself, you can do so in a variety of ways. In the below test app, I use appendChild
to add sub-elements to the root of the XML document. You might do something similar if you needed to construct an XML file for saving to disk (in the case of AIR) or passing to an XML-based web API.
In contrast to XML, Object
and Dictionary
are used all the time by AS3 programmers in need of a generalized key-value map. These are pure data structures that are less related to a file format than XML is, but are often the format of choice for generic data file parsers such as those decoding JSON files.
The below test app creates some large XML
, Object
, and Dictionary
and measures the time it takes do do so. It then reads all of the elements of each collection object to test the access speed.
package { import flash.display.*; import flash.utils.*; import flash.text.*; public class XMLSpeed extends Sprite { private var __logger:TextField = new TextField(); private function log(msg:*): void { __logger.appendText(msg + "\n"); } public function XMLSpeed() { __logger.autoSize = TextFieldAutoSize.LEFT; addChild(__logger); const SIZE:int = 10000; var beforeTime:int; var afterTime:int; var i:int; var xml:XML = <root/>; var obj:Object = {}; var dict:Dictionary = new Dictionary(); log("Build"); log("Container,Time"); beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { xml.appendChild("child"+i); } afterTime = getTimer(); log("XML," + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { obj["child"+i] = true; } afterTime = getTimer(); log("Object," + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { dict["child"+i] = true; } afterTime = getTimer(); log("Dictionary," + (afterTime-beforeTime)); log(""); log("Access"); log("Container,Time"); beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { xml.child("child"+i); } afterTime = getTimer(); log("XML," + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { obj["child"+i]; } afterTime = getTimer(); log("Object," + (afterTime-beforeTime)); beforeTime = getTimer(); for (i = 0; i < SIZE; ++i) { dict["child"+i]; } afterTime = getTimer(); log("Dictionary," + (afterTime-beforeTime)); } } }
Here is the test environment I ran this app on:
- Flex SDK (MXMLC) 4.1.0.16076, compiling in release mode (no debugging or verbose stack traces)
- Release version of Flash Player 10.2.154.27
- 2.4 Ghz Intel Core i5
- Mac OS X 10.6.7
And here are the results I got:
Build
Container | Time |
---|---|
XML | 4277 |
Object | 10 |
Dictionary | 7 |
Access
Container | Time |
---|---|
XML | 2126 |
Object | 6 |
Dictionary | 5 |
The results are even more striking in graph form:
At both creation and access, XML is tremendously slower than either Object
or Dictionary
. If you’re keeping around an XML
object that you’ve loaded and querying it on a regular basis (many times per frame, for example), consider either converting the XML
object to a plain Object
or Dictionary
or, for the ultimate speed boost, storing them as typed fields of a class. At a performance penalty of 400x, it may very well be worth it all on its own.
#1 by lordB8r on May 9th, 2011 ·
Hey Jackson,
Interesting find. My only question is about your creation of XML, because your object and dictionary each get assigned a boolean, what does that do for the performance of the xml if you assign the child a value as well (and not just append the child). Otherwise, thanks for the post.
LordB8r
#2 by jackson on May 9th, 2011 ·
They are, I admit, a little different.
XML
by nature represents a well-formed XML document and is therefore not a pure key-value map likeObject
andDictionary
. As such, it’s necessary to create child elements in order to do mapping. In the test, I attempted to simulate creating a map withSIZE
key/value pairs with as little overhead as possible. As such, I wanted to keep the value as simple as possible, so I used booleans forObject
andDictionary
and empty elements (i.e.<childN/>
) for XML. The XML version is, as you can see from the article and understand based on the amount of extra work is must necessarily do behind the scenes, quite a bit more expensive. I could have attempted to load up theObject
andDictionary
test with more and more expensive values, but this would have meant that they would be holding much more data than theirXML
equivalent. Actually, by holding aBoolean
value, they already hold more data than an empty child node.If I understood your question all wrong, please feel free to post a code snippet showing what you think would be a more fair test. I’m always open to suggestions. :)
#3 by Paul on May 11th, 2011 ·
Interesting. I’ve never really used the Dictionary object for storing data. Normally just use arrays, or access the xml.
I’m not sure if his example has much application in the real world though. How often do you really have xml data that simple? Normally you have nodes, child nodes, attributes, etc. How much processing would you have to do to sift all that out so you can store it in a Dictionary object in a logical manner?
Take for example this nav xml:
(EDIT by Jackson: removed XML. It was a simple XML document with all elements directly under the root element.)
I query the nav xml to create XMLList objects twice. I fetch the swfs and store them in an array. Then, the xml is passed to another class where it is queried to create the buttons, which get stored in an array. If I pull that stuff out of the xml to save that data in a dictionary object, those same pulls still need to happen, right? Where would we gain?
Same project, different xml file—This one much more complex:
(EDIT by Jackson: removed XML. It was an XML document with two levels of nesting.)
For the rooms-setup.xml file, how would you—or why would you—convert that data to an object or dictionary? Normally I query the xml to make XMLLists to loop through and create objects which are stored in arrays, or to extract data to be stored in arrays. For example, I’ll loop through _xmlVar..room and send each room node to Room.as. In Room.as, I’ll loop through _roomXML..roomItem to fetch the swfs and pass the other data to other arrays, or generate other objects. Once the data is extracted, the xml is left alone.
You could probably loop through the nodes and loop through the attributes on each node and store in an object or dictionary, but you still have to access all that data in the xml, which is apparently slow. Then accessing that data later would be a pain—you’d have to loop through those dictionaries or objects instead of being able to say _roomsXML..room, or _roomsXML..sectionLanding to create lists you can loop through to create objects.
Maybe I don’t understand the Dictionary, but seems like an extra step and that it would create complexity and delays when you want to access things later. If you have more information on how to and when to use Dictionaries I’d love to see it.
Thanks,
Paul
#4 by jackson on May 11th, 2011 ·
You’re right that if you’re simply accessing the
XML
object once then there is no point to converting it toDictionary
then accessing that. Such a conversion necessarily entails the same accesses as well as extra allocation. The performance boost comes with you are accessing the XML data repeatedly throughout an application. For example, if you have anENTER_FRAME
listener that loops over anXML
object, you may very well end up doing lots lots of very expensive accesses. Remember that even 10,000 accesses in the test on a pretty quick CPU took 2,000 milliseconds to complete. That’s 0.2 milliseconds per access. At this rate, even 10 accesses 30 times per second (i.e. 30 FPS) would take 60 milliseconds and effectively limit your framerate to 16-17 just to do the XML accesses. This is the performance bottleneck that usingDictionary
can help solve. If you can build aDictionary
that’s equivalent to yourXML
object, then you can take advantage of the much faster performance and allow your app to run smoothly again. In the case of more complicated XML documents, you may need to nestDictionary
objects inside ofDictionary
objects to achieve the desired effect. The performance difference is so drastic that even doing several access on aDictionary
object is still vastly preferable to a singleXML
access.#5 by Paul on May 11th, 2011 ·
Doh! guess I should have embedded that as xml instead of actionscript3. I’ll re-embed here. if you could merge, that’d be great.
Thanks, and Sorry.
Paul
(EDIT by Jackson: removed XML and cleaned up previous comment)
#6 by Paul on May 11th, 2011 ·
OK. the xml type on the code embed didn’t work either. Not sure how to fix it…. :-/
(EDIT by Jackson: removed XML)