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:
- Accessing first element of collection: filmsXML.film;
- Accessing whole collection: filmsXML.film;
- 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.
|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.
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.