Note: Unfortunately, the images that accompanied this post were stored in an individual’s private share and have been lost. Because this post is very old, it will not be updated. However, the results of this work did make its way into SproutCore as
Traditionally, when you include a script tag in your page, the browser will stop processing the page while it downloads, evaluates, and executes your script. Even if your code doesn’t do much but define classes and other standards, simply parsing and executing such a large quantity of code can have a noticeable effect. This is especially true on mobile devices like the iPhone.
My first tests were micro-benchmarks focused on the different types of deferred evaluation. They seemed to indicate that using this technique should reduce the JS portion of your page load 3x-10x. Comments vs strings don’t seem to make a difference, so I’m focusing on strings because it is easier to process later.
Those were micro-benchmarks. Next I wanted to see how this would play out with a real page.
Each test does the same basic thing: it loads jQuery then generates a report with the timing results. Here are how the tests differ:
- Baseline – load jQuery like normal via script tag. jQuery is parsed and executed immediately on load.
- Closure – load jQuery in a closure but don’t actually execute the closure until after the onload event fires. This essentially means the jQuery code will be parsed but not executed until later.
- String – load jQuery as a giant string. After the onload event fires, eval() the string to actually make jQuery ready for use.
The test measures two times: load and setup. The load time is the time that elapsed from the start of the page load to when the onload event fired. setup is the time that elapsed from the start of the page load until jQuery was fully loaded and ready to use.
For the baseline test, the load and setup times are always the same (since jQuery is setup while it loads). The closure and string tests both have to do some extra work after onload fires to prepare jQuery, so their setup times will differ.
The test code, by the way, is setup to repeat five times before it generates a report. If you ran the test, you would see it reload the page several times. This is why.
So how did it do? As expected, deferred evaluation cut load time significantly – sometimes by up to 90%. Surprisingly, in all browsers except for Chrome, the total time required to get jQuery setup and ready to use was roughly the same. This means that the overall cost of using deferred evaluation to load your code is not nearly as bad as my earlier microbenchmarks implied.
Here are the relevant diagrams. All numbers are relative to the time required to load the baseline for each browser. This makes it easier to see the difference:
[I don’t have access to IE this weekend. I will add a chart for IE soon.]
So what’s going on with this Chrome business? Apparently Chrome is almost 4x slower at eval()’ing code than it is as loading the exact same code from a script tag.
That said, Chrome is ridiculously fast at loading scripts. In absolute time, Chrome took an average of 12 milliseconds to load jQuery in the baseline test. The next closest was WebKit with 36 milliseconds. In the “string” test, Chrome takes about 50 msec compared to WebKit’s 45 msec.
In other words, Chrome appears to have a fast path for loading scripts from a script tag that is triggered by my test. When using other techniques it is “only” as fast as WebKit – which beats pretty much everyone else by a country mile so I wouldn’t worry too much about it.
You should not use deferred evaluation to load all of your code, especially any code you know you will need on page load anyway. Since you will eval() the code in the end, there is no win.
For SproutCore, we already have support for deferred evaluation built in. It will be included in the main branch for SproutCore 1.1 sometime in early 2010. You can try it today by using the “tiki” branches of both the build tools and sproutcore itself.
UPDATE: I had a chance to test IE8 today as well. Here are the results (IE7 had a similar profile):
Basically, the net of this chart means that the string approach won’t actually save you as much time with IE. As you can see the base load time is about the same for both methods. When you add in the extra eval(), the string method is actually about 13% slower than just executing your code outright.
That said, there are some confounding factors to keep in mind here:
- IE had much more variability in its results than all the other browsers. In some cases, the baseline was slower than the string – in other cases it was faster by a wide margin. Overall I would rate these as about equal in performance.
- If you don’t actually use the code you are loading, the string method is still no worse than baseline loading.
In summary, my original advice still stands. Deferred evaluation can be very useful if you need to load a lot of code that you may or may not use. This is especially true for mobile devices. With IE you will receive fewer benefits however. If your audience is primarily IE it might not be worth the effort.Tweet