Wednesday, November 1st, 2006

Optimizing Page Load Time

Category: Articles

>Aaron Hopkins of Google has released an article on Optimizing Page Load Time which came out of his experience optimizing page load times for a high-profile Ajax application.

He starts off talking about “how much I could reduce latency due to external objects. Specifically, I looked into how the HTTP client implementation in common browsers and characteristics of common Internet connections affect page load time for pages with many small objects.”

  • Neither IE nor Firefox ship with HTTP pipelining
    enabled by default. This means each request has to be answered and its
    connection freed up before the next request can be sent. This incurs
    average extra latency of the round-trip (ping) time to the user divided by
    the number of connections allowed. Or if your server has HTTP keepalives
    disabled, doing another TCP three-way handshake adds another round trip,
    doubling this latency.

  • By default, IE allows only two outstanding connections per hostname
    when talking to HTTP/1.1 servers or eight-ish outstanding connections total.
    Firefox has similar limits. Using up to four hostnames instead of one will
    give you more connections. (IP addresses don’t matter; the hostnames can
    all point to the same IP.)

  • Most DSL or cable Internet connections have asymmetric bandwidth, at
    rates like 1.5Mbit down/128Kbit up, 6Mbit down/512Kbit up, etc. Ratios of
    download to upload bandwidth are commonly in the 5:1 to 20:1 range. This
    means that for your users, a request takes the same amount of time to send
    as it takes to receive an object of 5 to 20 times the request size.
    Requests are commonly around 500 bytes, so this should significantly impact
    objects that are smaller than maybe 2.5k to 10k. This means that serving
    small objects might mean the page load is bottlenecked on the users’
    upload bandwidth, as strange as that may sound.

He needed to measure the effective bandwidth of his users, and used the following to track it via his logs:

  1. <html>
  2. <head>
  3. <title>...</title>
  4. <script type="text/javascript">
  5. <!--
  6. var began_loading = (new Date()).getTime();
  7. function done_loading() {
  8. (new Image()).src = '/timer.gif?u=' + self.location + '&t=' +
  9.  (((new Date()).getTime() - began_loading) / 1000);
  10. }
  11. // -->
  12. </script>
  13. <!--
  14. Reference any external javascript or stylesheets after the above block.
  15. // -->
  16. </head>
  17. <body onload="done_loading()">
  18. <!--
  19. Put your normal page content here.
  20. // -->
  21. </body>
  22. </html>

He then came up with tips to reduce page load time:

  • Turn on HTTP keepalives for external objects. Otherwise you add an extra round-trip for every HTTP request. If you are worried about hitting global server connection limits, set the keepalive timeout to something short, like 5-10 seconds. Also look into serving your static content from a different webserver than your dynamic content. Having thousands of connections open to a stripped down static file webserver can happen in like 10 megs of RAM total, whereas your main webserver might easily eat 10 megs of RAM per connection.
  • Load fewer external objects. Figure out how to globally reference the same one or two javascript files and one or two external stylesheets instead of many; try preprocessing them when you publish them. If your UI uses dozens of tiny GIFs all over the place, consider switching to CSS, which tends to not need so many of these.
  • If your users regularly load a dozen or more uncached or uncachable objects per page, consider evenly spreading those objects over four hostnames. This usually means your users can have 4x as many outstanding connections to you. Without HTTP pipelining, this results in their average latency dropping to about 1/4 of what it was before.
  • Allow static images, stylesheets, and javascript to be cached by the browser. This won’t help the first page load for a new user, but can substantially speed up subsequent ones.

    Set an Expires header on everything you can, with a date days or even months into the future. This tells the browser it is okay to not revalidate on every request, which can add latency of at least one round-trip per object per page load for no reason.

  • Minimize HTTP request size. Often cookies are set domain-wide, which
    means they are also unnecessarily sent by the browser with every image request
    from within that domain. What might’ve been a 400 byte request for an image
    could easily turn into 1000 bytes or more once you add the cookie headers.
    If you have a lot of uncached or uncachable objects per page and big,
    domain-wide cookies, consider using a separate domain to host static
    content, and be sure to never set any cookies in it.

  • Minimize HTTP response size by enabling gzip compression for HTML and
    XML for browsers that support it. For example, the 17k document you are
    reading takes 90ms of the full downstream bandwidth of a user on 1.5Mbit
    DSL. Or it will take 37ms when compressed to 6.8k. That’s 53ms off of the
    full page load time for a simple change. If your HTML is bigger and more
    redundant, you’ll see an even greater improvement.

    If you are brave, you could also try to figure out which set of browsers
    will handle compressed Javascript properly. (Hint: IE4 through IE6 asks for
    its javascript compressed, then breaks badly if you send it that way.) Or
    look into Javascript obfuscators that strip out whitespace, comments, etc
    and usually get it down to 1/3 to 1/2 its original size.

  • Consider locating your small objects (or a mirror or cache of them)
    closer to your users in terms of network latency. For larger sites with a
    global reach, either use a commercial Content Delivery
    Network
    , or add a
    colo within 50ms of 80% of your users and use one of the many available
    methods
    for routing user requests to your colo nearest them.

  • Regularly use your site from a realistic net connection. Convincing
    the web developers on my project to use a “slow proxy” that simulates bad
    DSL in New Zealand (768Kbit down, 128Kbit up, 250ms RTT, 1% packet loss)
    rather than the gig ethernet a few milliseconds from the servers in the U.S.
    was a huge win. We found and fixed a number of usability and functional
    problems very quickly.

  • (Optional) Petition browser vendors to turn on HTTP pipelining
    by default on new browsers. Doing so will remove some of the need for these
    tricks and make much of the web feel much faster for the average user.
    (Firefox has this disabled supposedly because some proxies and some versions
    of IIS choke on pipelined requests. But there are workarounds.)

Related Content:

Posted by Dion Almaer at 7:23 am
21 Comments

+++--
3.9 rating from 59 votes

21 Comments »

Comments feed TrackBack URI

I sometimes feel that posts on Ajaxian replicate a little too much of the source material that they are linking to.

Comment by Simon Willison — November 1, 2006

I agree with Simon.

Comment by peter — November 1, 2006

I didn’t even see the above comments (read the entry via RSS) and thought the same thing.

Comment by Jeffrey — November 1, 2006

I really think this post skirts the bounds of fairness. This is just a copy of somebody else’s work, with silly, token phrases around it.

Comment by Scott — November 1, 2006

mind you, I was going not to read the original article (kinda thinking that I could run and Ajaxian-clone as well), but it is actually much better than this cut-down extract…

Comment by Sad developer — November 1, 2006

At least there’s a link in this one, and you can post comments. The odd entry that mentions someone else but doesn’t provide a link, or the entry that shows up on the home page of ajaxian but when you click through it says “page not found” — now that’s annoying.

Comment by Andy — November 1, 2006

I’d love to find out more about the “slow proxy” he mentioned. I’ve been trying to simulate that for years… between keeping dial up accounts, or rigging up serial connected computers. I’d love to see docs on how to do that.

Comment by Matt Nuzum — November 1, 2006

Hey I’m just glad they posted it, it’s great content.

FYI, using Amazon S3 for media accomplishes most of the key tips in one shot.
S3 globally distributes content to get it close to your users, the service supports Expires headers, it is already one additional hostname and you can set up more aliases, and it won’t have cookies unless you set some.
We use it at SmartClient.com, where we wanted a plain HTML landing page so you immediately have something to read, and then we cache our AJAX framework and media in the background.

Comment by Charles Kendrick — November 1, 2006

Seitenladezeit optimieren

Wie gerne ein Internetdienst genutzt wird hängt nicht nur vom Funktionsumfang und der Darstellung ab. Auch Antwortzeiten sind trotz immer schneller werdender Netzanbindungen relevant. Leider wird dies meiner Ansicht nach sehr vernachlässigt, da man auf

Trackback by .: blogging augusto :. — November 1, 2006

[...] I have noticed that it’s not so much the size of the things I load as the number of things that plays in to how much time the loading takes, which is, among other things, explained in this article on Ajaxian (it’s based on another article which you can find here: Optimizing page load time). [...]

Pingback by Iconara » Blog Archive » Speed up that loader! — November 1, 2006

[...] Ajaxian » Optimizing Page Load Time Статья об оптимизации загрузки страниц (tags: javascript html вебдев) [...]

Pingback by Binary Look » links for 2006-11-01 — November 1, 2006

Way to steal traffic from die.net. Perhaps you should consider pointing us to the original site and leaving out the wholesale paraphrasing? Weak, Ajaxian. Very, very weak.

Comment by Steve — November 1, 2006

They linked to the author from where they found the information. Enough already.

Comment by Mario — November 1, 2006

Wow,

I read the portion in quotes and thought “wow that’s a lot of quoting” only to find out the EVERYTHING under the code snippet is word for word from the source.

Didn’t really expect this from these guys…wow….

Comment by Timmy — November 1, 2006

Now this is useful.
Refactoring is one of the biggest part of coding. JavaScript is one of the hardest one to maintain good memory usage.

Comment by Simon Jia — November 2, 2006

[...] Or if you are familiar with the HTTP protocol and TCP/IP at the packet level, you can watch what is going on using tcpdump, ngrep, or ethereal. These tools are indispensible for all sorts of network debugging. (Optional) Petition browser vendors to turn on HTTP pipelining by default on new browsers. Doing so will remove some of the need for these tricks and make much of the web feel much faster for the average user. (Firefox has this disabled supposedly because some proxies, some load balancers, and some versions of IIS choke on pipelined requests. But Opera has found sufficient workarounds to enable pipelining by default. Why can’t other browsers do similarly?) The last tip in my opinion is not a really good one, Browser developers will most likely just ignore you, and you wont be able to change the world. For the full analysis, with graphics and the full discussion please see the entry by Aaron, it’s definitely one of the best readings in the last months, with all the technical details [via Ajaxian]Technorati Tags: optimization, web 2.0, google [...]

Pingback by Fading Roses & Raging Viruses » Optimizing Page Load Time — November 3, 2006

[...] Or if you are familiar with the HTTP protocol and TCP/IP at the packet level, you can watch what is going on using tcpdump, ngrep, or ethereal. These tools are indispensible for all sorts of network debugging. (Optional) Petition browser vendors to turn on HTTP pipelining by default on new browsers. Doing so will remove some of the need for these tricks and make much of the web feel much faster for the average user. (Firefox has this disabled supposedly because some proxies, some load balancers, and some versions of IIS choke on pipelined requests. But Opera has found sufficient workarounds to enable pipelining by default. Why can’t other browsers do similarly?) The last tip in my opinion is not a really good one, Browser developers will most likely just ignore you, and you wont be able to change the world. For the full analysis, with graphics and the full discussion please see the entry by Aaron, it’s definitely one of the best readings in the last months, with all the technical details [via Ajaxian]Technorati Tags: optimization, web 2.0, google [...]

Pingback by Fading Roses & Raging Viruses » Optimizing Page Load Time — November 3, 2006

[...] http://ajaxian.com/archives/optimizing-page-load-time [...]

Pingback by 本日書籤 « penk - Keep on rockin’ in the free world — November 4, 2006

[...] die.net: Optimizing Page Load Time Too complex for me… [...]

Pingback by Filter for 2/11 2006 - Felt — November 10, 2006

Preventing memory leaks is very hard to do.

Comment by Justin — May 8, 2007

Simon Willison has made a good point that sometimes posts on Ajaxian replicate a little too much of the source material that they are referring to.

Comment by Danny — November 20, 2007

Leave a comment

You must be logged in to post a comment.