Ajax Performance
A blog by Ryan Breen of Gomez
Homeward Bound
December 21, 2006 on 9:44 am | In personal | No CommentsWe’re packing up the car for the 15 hour drive (Google Maps tends to be pessimistic) from Boston to Charleston, SC. I should have some time to post and respond to comments between now and the New Year, but today and next Friday will be consumed by the drive.
A brief coda on connection management: HTTP 1.0
December 21, 2006 on 2:09 am | In http | 3 CommentsIn my previous article, I briefly mentioned that per-host connection limits are different for HTTP 1.0 vs HTTP 1.1, and I suggested that you almost always want to use HTTP 1.1. Today, I will explore why and highlight one tiny scenario where HTTP 1.0 might still be useful.
While IE and Firefox both default to two concurrent persistent connections, the limits are a bit less stringent for HTTP 1.0. IE allows 4 concurrent HTTP 1.0 connections, while about:config tells me that Firefox currently allows 8. So, without any DNS hackery, HTTP 1.0 gives you at least twice as many connections to work with, but it comes at a substantial cost.
To understand why, let’s revisit our goals from the previous connection discussion. We were talking about optimizing your connection layout to benefit the modern population of users on broadband connections for whom the time to set up a connection and request content is a much higher percentage of the total transfer time than the actual content download. Persistent connections allow us to minimize the cost of connection setup, since we only have to do that once for each connection to the server, and the DNS hack lets us parallelize the requests so we have fewer stalls waiting for the server to reply to our requests. Switching to HTTP 1.0 is typically a significant step back as you now need to eat that connection cost for each request to the server.
I can think of only one case where the gain would outweigh the costs. For a site with a small number of large objects or a small number of objects that require server side content generation (which tends to elongate the time from when we send our request till we start receiving data), the cost of creating the connections will be a smaller chunk of the total latency. Switching to HTTP 1.0 may give an effective gain in throughput without requiring you to monkey around with DNS.
In the time I’ve been in the performance space, I’ve seen only one customer use this technique. They had very specific constraints — a few large objects — so they got a nice win with HTTP 1.0. For all other cases, HTTP 1.1 is unambiguously the way to go.
Another great article on combining and compressing JavaScript files
December 20, 2006 on 10:11 am | In ajax | No CommentsNiels Leenheer has written a great article exploring the benefits of compressing and combining JavaScript include files, a topic I’ve mentioned a few times here. But Niels went several extra steps by building a PHP script to automate the entire process.
Brilliant stuff, and hopefully we can collect some hard data in the coming days to show the power of this approach.
Circumventing browser connection limits for fun and profit
December 18, 2006 on 11:14 pm | In ajax | 46 CommentsA few days ago, this video hosted by metacafe popped up on digg, explaining how to increase site download times by tweaking your browser settings to increase connection parallelism. To explain why this works, let’s step back a bit to discuss how browsers manage server connections.
In building any application, developers are often required to make ‘utilitarian’ choices. Pretentiously paraphrasing Jeremy Bentham, ‘utilitarian’ describes an approach that ‘does the greatest good for the greatest number.’ Many times, sacrifices in performance are made for a subset of users so that the average expected performance of all users will be better.
Since all browsers were originally developed for a time when the vast majority of users were on bandwidth-constrained dial-up links, it made sense to restrict users to a small number of connections. The overhead of juggling many connections over dial-up makes it difficult for progress to be made servicing any of the individual requests. Also, web servers and proxy servers in that era were less robust, so keeping the per-browser connection pool small reduced the risk of overwhelming the network infrastructure.
To find a decent balance, IE and Firefox by default restrict users to 6 connections total and 2 connections per host for HTTP 1.1 connections. HTTP 1.0 is a slightly different story, but the benefits of persistent connections means that you should be (and probably are) using HTTP 1.1 anyway.
Of course, in the real world, these utilitarian decisions have a tendency to rot, outliving the time when they are relevant. Today, the majority of users have broadband connections, so client side bandwidth is not the gating factor. Typically, latency in retrieving individual objects is dominated by the time required to setup a connection and send a request. By increasing the number of concurrent connections, we can parallelize that cost and churn through the list of pending objects more quickly, leading to an increase in user perceived download time. “Lightning fast,” to echo the hyperbole of the metacafe clip.
Unfortunately, relying on your users to modify their browsers is not a reasonable optimization strategy, so what can the concerned developer do to tap into these gains?
Most sites only use one host, so requests are forced to share the 2 connections to that host. One effective strategy to improve parallelism is to spread your content out across multiple hosts. This is not hard to do as browsers look at the hostname, not the ip. That is, images1.yoursite.com and images2.yoursite.com would each be allowed 2 connections.
As a practical example, check out this sample application Buddy and I built for our workshop at Web Builder 2.0. The album thumbnails are by default all loaded from the parent host, so they all share from two connections. Below is a sample ‘waterfall’ chart of the objects loaded for this page, collected by Gomez’ external performance testing service.
The full version of the chart is available behind the link.
You can see from the graphic that only two connections (C0 and C2) are opened for musicstore.ajaxperformance.com. We are using HTTP 1.1, so we do not need to open a separate connection for each image, but we are still spending the majority of our time servicing object requests. The object times are dominated by the cost of requesting the images (the blue first byte time) and very little time is spent downloading the content (the red content time).
To improve performance, we created CNAMEs for images1.ajaxperformance.com, images2.ajaxperformance.com, and images3.ajaxperformance.com, all of which point back to our main host. This small snippet in our Rails album layout code spread each album image across a different host:
img_url = "/images/album_art/#{album.id}.jpg"
if @perf_multiplex_images
idx = (album.id % 3) + 1
img_url = "http://images#{idx}.ajaxperformance.com/images/album_art/#{album.id}.jpg"
end
You can see the results by hitting the updated version. First load performance should be much better. As you can see from the below waterfall (again, the full chart is available behind the link), we are now using 6 connections to grab our images.
So what did that buy us? A lot, given the relatively simple change. Below is a head-to-head comparison of total page load time, collected over 24 hours from 8 locations around the country.
The average load time when using 2 connections is 7.919 seconds. The average load time when using 6 connections is 4.629 seconds. That’s a greater than 40% drop in page load time. This technique will work anywhere that you have a large block of object requests currently served by one host.
There is plenty of precedent for this approach in real world Ajax apps. To exploit connection parallelism, the image tiles at Google Maps are served from mt0.google.com through mt3.google.com. Virtual Earth also uses this technique.
You can also use this connection management approach to sandbox the performance of different parts of your application. If you have page elements that require database access and may be more latent than static objects, keep them from clogging up the 2 connections for image content by putting them on a subdomain. This trick won’t cause a huge improvement in the total load time of your page, but it can significantly improve the perceived performance by allowing static content to load unfettered.
Gzip and Caching love for Tapestry
December 4, 2006 on 11:51 pm | In ajax | No CommentsTapestry developer Jesse Kuhnert knocked out a tasty update, tuning cache parameters and adding liberal use of gzip to yield a bundled dojo kit that is 25% of its previous size over the wire. Also, text responses from Ajax and json requests are compressed automatically.
There are some great lessons in this article (and in the links he provides to background resources).
Off to Vegas
December 3, 2006 on 2:30 pm | In ajax | No CommentsBuddy and I are off to Vegas for Web Builder 2.0. I will be presenting on Tuesday afternoon, and Buddy and I are hosting a full day Ajax performance workshop on Wednesday.
A promising ‘Performance Research’ series from the YUI blog
December 3, 2006 on 2:19 pm | In ajax | 1 CommentThis article, the first in a series, discusses the performance impact of the number of HTTP requests, concluding that reducing this number is the most cost-effective way to reduce response time. This is consistent with my own experience as well as the recommendations from Brad Neuberg’s (frequently cited here) Ajax optimization tutorial.
The next article from the YUI guys will deal with caching. It’s great to see so many more performance articles these days — it feels like we are turning the corner as more developers focus on how to make Ajax work well rather than just making it work at all.
Powered by WordPress with Pool theme design by Borja Fernandez.
Entries and comments feeds.
Valid XHTML and CSS. ^Top^


