ActionScript 3 timeout problems when working with XML

I was getting the following error while developing my doodle flash application using ActionScript 3.0:

1502 A script has executed for longer than the default timeout period of 15 seconds.

I tracked the problem down to a piece of code that looped through a large XML document. At first I thought there was nothing I could do, but after playing about with it and pulling my hair out for a while I found out several things that helped me reduce the runtime by an impressive amount. I wanted to share my findings in case anyone else was having similar problems.

Finding 1: XML .. operator is very slow

ActionScript 3's new XML notation is fantastic, but I got a bit carried away with the .. operator, also known as the descendant accessor.

The descendant accessor returns a collection of all nodes in an XML node that have a specified name. It looks a bit like this:

filmDirectors = filmCollectionXML..director;

In this example filmDirectors now holds a collection of all director nodes. The .. operator searches through all filmCollectionXML's nodes and their child nodes for any node called director.

If you need to search for stuff this is great, but I was using it as shorthand when I knew where the nodes were, but they were quite deep in the XML.

I ran some tests and found that in a 250 node XML document using the descendant accessor to get a property took on average 0.7ms, where as accessing it directly took 0.2ms. That's 3.5 times as much time and when you're looping through an XML document, this can become a big deal. Also, using .. gets slower, the bigger the XML document is. Getting rid of .. from my XML loop fixed my crashing problem.

So, don't use the descendant accessor as shorthand. Only use it if you have a good reason to.

Finding 2: Functions in a loop condition are run on every iteration

This is obvious really, but it's easy to overlook. I had a loop header that looked a bit like this:

for(var i:Number = 0; i < filmCollectionXML..director.length(); i++)

The loop condition i < filmCollectionXML..director.length() has two functions in it; .. and length(). These two functions will run on every iteration of the loop. The following will run a lot quicker:

var numDirectors = filmCollectionXML..director.length();
for(var i:Number = 0; i < numDirectors; i++)

And, it would be even quicker if we didn't need to use the .. notation at all, but using it just once is okay.

So, where possible, pre-compute the value of functions needed in loop conditions.

Finding 3: Things get slower the bigger the XML document is

If you're looping though a whole XML document then this will obviously take longer if the document is bigger. However, it's not so obvious that even accessing a single element of an XML document will take longer and longer the longer the XML document gets.

I ran some tests for the following operations:

  1. Accessing first element of collection: filmsXML.film[0];
  2. Accessing whole collection: filmsXML.film;
  3. Accessing length() of collection: filmsXML.film.length();

I found that they all took the same amount of time, but that the time to do them increased as the XML document got bigger. I did the same using the descendant accessor and found that it was even worse.

Time in milliseconds to perform XML operations
Number of XML nodes 500 1000 1500 2000
An XML operation 0.4 0.9 1.3 1.7
An XML .. operation 1.2 2.4 3.6 4.7

This graph shows it more clearly and how the time taken when using the descendant accessor increases at a quicker rate.

Graph showing how the time to do XML increases

So, try to limit the size of your XML documents and break them down into smaller ones if they're very large.

By using these three findings I was able to reduce the execution time from over 15 seconds to just over a second. I was very pleased

Gary said

Instead of doing what you do in the second loop, you could just use the for each construct instead: for each (var director:XML in filmCollectionXML..director) When you use a for each it only evaluates the xpath expression once.

Bijo said

Dear Richard, I went through your article and found it to be very interesting. Now, I'm having a similar problem in loading large xml files. I have a question bank of appr. 10,000 questions. I divided it into various unit xmls of average 1000 nodes. I'm using a custom class to store individual questions, options, the answer and the user answer. But while loading the questions from xml to the variable in the instance of the class, flash hangs saying, 1502 A script has executed for longer than the default timeout period of 15 seconds. Is there a better way to do this? Please help. Thanks, Bijo

Richard Garside said

@bijo if you've followed the advice in this article and you're still having problems, then I think your XML file is probably just too large. If I was you, I would split it into much smaller sections and load them 1 at a time as they're needed.

Gom said

I've solved same problem by using XMLList. Thank you for good posting.

Dave said

Oh man this was a lifesaver. I was using the descendant accessor all over the place and my compiler was choking on a 5500 line xml file. I optimized my loops as suggested and now it not only compiles without timing out but does so in a few seconds. thanks!