The “Self” column shows how long a function took to execute and the “Total” column shows how long a function and any nested functions took to execute. Selecting the “Top down” view and sorting the results by the “Total” column will give us the function call stack, listing the most expensive functions first.
The second item in the report shows the slowest code path, taking a total of 1.61 seconds to complete! We need to find out what’s taking so long.
The “Self” column shows that the function in question actually took no time to execute which rules it out as the culprit so it must be calling a slow function deeper in the call stack – we need to dig a little deeper to see what’s causing the problem.
Expanding the row reveals the functions that were directly called by the current function. The profiler view can become messy as you drill down through the function stack, you can make it a little easier on the eye by filtering out everything but the function you’re interested in by clicking the focus icon (the eye).
Following the same process as above, we can see that two functions
SearchLocationToggle.init were called.
SearchLocationToggle.init took no time to run and its child functions took no more than 1ms so we can rule this out, leaving us with “klass”.
We keep drilling down through the function calls until we see a high value in the “Self” column…
A function called
stripScripts is taking 710ms to execute – let’s start there.
Next to each function is a link to its definition. The link points to either
prototype.js, a 3rd party JS library or
<select> dropdown menu. It’s tempting to jump straight into the prototype.js file and look at making
stripScripts run faster but we need to look at our own code first.
Working back from
stripScripts we can see that
$$.each.grouped.each.list was the function in
fancy_selects.js that resulted in the function being called. Clicking the link reveals the following code:
This block of script recreates a
<select> menu option list using
<input> tags. It also binds event handlers for managing selection and hover states. If we switch back to the profile output we can see that
stripScripts is called by
Element.methods.insert (Element.insert), which is called 4 times in the above code.
A quick review of the page shows that this method is run on 7 different
<select> elements, containing 502 options which, when multiplied by the number of generated HTML elements, comes to 2008 elements and that doesn’t include text nodes used for holding the item values.
Fixing the problem
This method of creating a selectable list is very inefficient. There’s no need to create so many element objects to produce a static option list. It would be much faster to create a string of HTML and leverage event delegation to handle the user interaction.
We completely rewrote the dropdown control to improve both speed and accessibility. During the development performance profiles were run periodically to ensure we were making the correct decisions regarding speed. As you can see from the screenshot below the same work as above is achieved in around 50ms – a massive improvement.