You are previewing Even Faster Web Sites.

Even Faster Web Sites

Cover of Even Faster Web Sites by Steve Souders Published by O'Reilly Media, Inc.
  1. Even Faster Web Sites
  2. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  3. Credits
  4. Preface
    1. How This Book Is Organized
      1. JavaScript Performance
      2. Network Performance
      3. Browser Performance
    2. Conventions Used in This Book
    3. Comments and Questions
    4. Using Code Examples
    5. Safari® Books Online
    6. Acknowledgments
  5. 1. Understanding Ajax Performance
    1. Trade-offs
    2. Principles of Optimization
    3. Ajax
    4. Browser
    5. Wow!
    6. JavaScript
    7. Summary
  6. 2. Creating Responsive Web Applications
    1. What Is Fast Enough?
    2. Measuring Latency
      1. When Latency Goes Bad
    3. Threading
    4. Ensuring Responsiveness
      1. Web Workers
      2. Gears
      3. Timers
      4. Effects of Memory Use on Response Time
      5. Virtual Memory
      6. Troubleshooting Memory Issues
    5. Summary
  7. 3. Splitting the Initial Payload
    1. Kitchen Sink
    2. Savings from Splitting
    3. Finding the Split
    4. Undefined Symbols and Race Conditions
    5. Case Study: Google Calendar
  8. 4. Loading Scripts Without Blocking
    1. Scripts Block
    2. Making Scripts Play Nice
      1. XHR Eval
      2. XHR Injection
      3. Script in Iframe
      4. Script DOM Element
      5. Script Defer
      6. document.write Script Tag
    3. Browser Busy Indicators
    4. Ensuring (or Avoiding) Ordered Execution
    5. Summarizing the Results
    6. And the Winner Is
  9. 5. Coupling Asynchronous Scripts
    1. Code Example: menu.js
    2. Race Conditions
    3. Preserving Order Asynchronously
      1. Technique 1: Hardcoded Callback
      2. Technique 2: Window Onload
      3. Technique 3: Timer
      4. Technique 4: Script Onload
      5. Technique 5: Degrading Script Tags
    4. Multiple External Scripts
      1. Managed XHR
      2. DOM Element and Doc Write
    5. General Solution
      1. Single Script
      2. Multiple Scripts
    6. Asynchronicity in the Real World
      1. Google Analytics and Dojo
      2. YUI Loader Utility
  10. 6. Positioning Inline Scripts
    1. Inline Scripts Block
      1. Move Inline Scripts to the Bottom
      2. Initiate Execution Asynchronously
      3. Use Script Defer
    2. Preserving CSS and JavaScript Order
    3. Danger: Stylesheet Followed by Inline Script
      1. Inline Scripts Aren’t Blocked by Most Downloads
      2. Inline Scripts Are Blocked by Stylesheets
      3. This Does Happen
  11. 7. Writing Efficient JavaScript
    1. Managing Scope
      1. Use Local Variables
      2. Scope Chain Augmentation
    2. Efficient Data Access
    3. Flow Control
      1. Fast Conditionals
      2. Fast Loops
    4. String Optimization
      1. String Concatenation
      2. Trimming Strings
    5. Avoid Long-Running Scripts
      1. Yielding Using Timers
      2. Timer Patterns for Yielding
    6. Summary
  12. 8. Scaling with Comet
    1. How Comet Works
    2. Transport Techniques
      1. Polling
      2. Long Polling
      3. Forever Frame
      4. XHR Streaming
      5. Future Transports
    3. Cross-Domain
    4. Effects of Implementation on Applications
      1. Managing Connections
      2. Measuring Performance
      3. Protocols
    5. Summary
  13. 9. Going Beyond Gzipping
    1. Why Does This Matter?
    2. What Causes This?
      1. Quick Review
      2. The Culprit
      3. Examples of Popular Turtle Tappers
    3. How to Help These Users?
      1. Design to Minimize Uncompressed Size
      2. Educate Users
      3. Direct Detection of Gzip Support
  14. 10. Optimizing Images
    1. Two Steps to Simplify Image Optimization
    2. Image Formats
      1. Background
      2. Characteristics of the Different Formats
      3. More About PNG
    3. Automated Lossless Image Optimization
      1. Crushing PNGs
      2. Stripping JPEG Metadata
      3. Converting GIF to PNG
      4. Optimizing GIF Animations
      6. Progressive JPEGs for Large Images
    4. Alpha Transparency: Avoid AlphaImageLoader
      1. Effects of Alpha Transparency
      2. AlphaImageLoader
      3. Problems with AlphaImageLoader
      4. Progressively Enhanced PNG8 Alpha Transparency
    5. Optimizing Sprites
      1. Über-Sprite Versus Modular Sprite
      2. Highly Optimized CSS Sprites
    6. Other Image Optimizations
      1. Avoid Scaling Images
      2. Crush Generated Images
      3. Favicons
      4. Apple Touch Icon
    7. Summary
  15. 11. Sharding Dominant Domains
    1. Critical Path
    2. Who’s Sharding?
    3. Downgrading to HTTP/1.0
    4. Rolling Out Sharding
      1. IP Address or Hostname
      2. How Many Domains
      3. How to Split Resources
      4. Newer Browsers
  16. 12. Flushing the Document Early
    1. Flush the Head
    2. Output Buffering
    3. Chunked Encoding
    4. Flushing and Gzip
    5. Other Intermediaries
    6. Domain Blocking During Flushing
    7. Browsers: The Last Hurdle
    8. Flushing Beyond PHP
    9. The Flush Checklist
  17. 13. Using Iframes Sparingly
    1. The Most Expensive DOM Element
    2. Iframes Block Onload
    3. Parallel Downloads with Iframes
      1. Script Before Iframe
      2. Stylesheet Before Iframe
      3. Stylesheet After Iframe
    4. Connections per Hostname
      1. Connection Sharing in Iframes
      2. Connection Sharing Across Tabs and Windows
    5. Summarizing the Cost of Iframes
  18. 14. Simplifying CSS Selectors
    1. Types of Selectors
      1. ID Selectors
      2. Class Selectors
      3. Type Selectors
      4. Adjacent Sibling Selectors
      5. Child Selectors
      6. Descendant Selectors
      7. Universal Selectors
      8. Attribute Selectors
      9. Pseudo-Classes and Pseudo-Elements
    2. The Key to Efficient CSS Selectors
      1. Rightmost First
      2. Writing Efficient CSS Selectors
    3. CSS Selector Performance
      1. Complex Selectors Impact Performance (Sometimes)
      2. CSS Selectors to Avoid
      3. Reflow Time
    4. Measuring CSS Selectors in the Real World
  19. A. Performance Tools
    1. Packet Sniffers
      1. HttpWatch
      2. Firebug Net Panel
      3. AOL Pagetest
      4. VRTA
      5. IBM Page Detailer
      6. Web Inspector Resources Panel
      7. Fiddler
      8. Charles
      9. Wireshark
    2. Web Development Tools
      1. Firebug
      2. Web Inspector
      3. IE Developer Toolbar
    3. Performance Analyzers
      1. YSlow
      2. AOL Pagetest
      3. VRTA
      4. neXpert
    4. Miscellaneous
      1. Hammerhead
      3. Cuzillion
      4. UA Profiler
  20. Index
  21. About the Author
  22. Colophon
  23. SPECIAL OFFER: Upgrade this ebook with O’Reilly
  24. Copyright

Chapter 1. Understanding Ajax Performance

Douglas Crockford

Premature optimization is the root of all evil.

Donald Knuth


The design and construction of a computer program can involve thousands of decisions, each representing a trade-off. In difficult decisions, each alternative has significant positive and negative consequences. In trading off, we hope to obtain a near optimal good while minimizing the bad. Perhaps the ultimate trade-off is:

I want to go to heaven, but I don’t want to die.

More practically, the Project Triangle:

Fast. Good. Cheap. Pick Two.

predicts that even under ideal circumstances, it is not possible to obtain fast, good, and cheap. There must be a trade-off.

In computer programs, we see time versus memory trade-offs in the selection of algorithms. We also see expediency or time to market traded against code quality. Such trades can have a large impact on the effectiveness of incremental development.

Every time we touch the code, we are trading off the potential of improving the code against the possibility of injecting a bug. When we look at the performance of programs, we must consider all of these trade-offs.

Principles of Optimization

When looking at optimization, we want to reduce the overall cost of the program. Typically, this cost is the perceived execution time of the program, although we could optimize on other factors. We then should focus on the parts of the program that contribute most significantly to its cost.

For example, suppose that by profiling we discover the cost of a program’s four modules.











If we could somehow cut the cost of Module B in half, we would reduce the total cost by only 2%. We would get a better result by cutting the cost of Module A by 10%. There is little benefit from optimizing components that do not contribute significantly to the cost.

The analysis of applications is closely related to the analysis of algorithms. When looking at execution time, the place where programs spend most of their time is in loops. The return on optimization of code that is executed only once is negligible. The benefits of optimizing inner loops can be significant.

For example, if the cost of a loop is linear with respect to the number of iterations, then we can say it is O(n), and we can graph its performance as shown in Figure 1-1.

Performance of a loop

Figure 1-1. Performance of a loop

The execution time of each iteration is reflected in the slope of the line: the greater the cost, the steeper the slope. The fixed overhead of the loop determines the elevation of its starting point. There is usually little benefit in reducing the fixed overhead. Sometimes there is a benefit in increasing the fixed overhead if the cost of each increment can be reduced. That can be a good trade-off.

In addition to the plot of execution time, there are three lines—the Axes of Error—that our line must not intersect (see Figure 1-2). The first is the Inefficiency line. Crossing this line reduces the user’s ability to concentrate. This can also make people irritable. The second is the Frustration line. When this line is crossed, the user is aware that he is being forced to wait. This invites him to think about other things, such as the desirability of competing web applications. The third is the Failure line. This is when the user refreshes or closes the browser because the application appears to have crashed, or the browser itself produces a dialog suggesting that the application has failed and that the user should take action.

The Axes of Error

Figure 1-2. The Axes of Error

There are three ways to avoid intersecting the Axes of Error: reduce the cost of each iteration, reduce the number of iterations, or redesign the application.

When loops become nested, your options are reduced. If the cost of the loop is O(n log n), O(n2), or worse, reducing the time per iteration is not effective (see Figure 1-3). The only effective options are to reduce n or to replace the algorithm. Fiddling with the cost per iteration will be effective only when n is very small.

Performance of a nested loop

Figure 1-3. Performance of a nested loop

Programs must be designed to be correct. If the program isn’t right, it doesn’t matter if it is fast. However, it is important to determine whether it has performance problems as early as possible in the development cycle. In testing web applications, test with slow machines and slow networks that more closely mimic those of real users. Testing in developer configurations is likely to mask performance problems.


Refactoring the code can reduce its apparent complexity, making optimization and other transformations more likely to yield benefits. For example, adopting the YSlow rules can have a huge impact on the delivery time of web pages (see

Even so, it is difficult for web applications to get under the Inefficiency line because of the size and complexity of web pages. Web pages are big, heavy, multipart things. Page replacement comes with a significant cost. For applications where the difference between successive pages is relatively small, use of Ajax techniques can produce a significant improvement.

Instead of requesting a replacement page as a result of a user action, a packet of data is sent to the server (usually encoded as JSON text) and the server responds with another packet (also typically JSON-encoded) containing data. A JavaScript program uses that data to update the browser’s display. The amount of data transferred is significantly reduced, and the time between the user action and the visible feedback is also significantly reduced. The amount of work that the server must do is reduced. The amount of work that the browser must do is reduced. The amount of work that the Ajax programmer must do, unfortunately, is likely to increase. That is one of the trade-offs.

The architecture of an Ajax application is significantly different from most other sorts of applications because it is divided between two systems. Getting the division of labor right is essential if the Ajax approach is to have a positive impact on performance. The packets should be as small as possible. The application should be constructed as a conversation between the browser and the server, in which the two halves communicate in a concise, expressive, shared language. Just-in-time data delivery allows the browser side of the application to keep n small, which tends to keep the loops fast.

A common mistake in Ajax applications is to send all of the application’s data to the browser. This reintroduces the latency problems that Ajax is supposed to avoid. It also enlarges the volume of data that must be handled in the browser, increasing n and again compromising performance.


Ajax applications are challenging to write because the browser was not designed to be an application platform. The scripting language and the Document Object Model (DOM) were intended to support applications composed of simple forms. Surprisingly, the browser gets enough right that it is possible to use it to deliver sophisticated applications. Unfortunately, it didn’t get everything right, so the level of difficulty can be high. This can be mitigated with the use of Ajax libraries (e.g., An Ajax library uses the expressive power of JavaScript to raise the DOM to a practical level, as well as repairing many of the hazards that can prevent applications from running acceptably on the many brands of browsers.

Unfortunately, the DOM API is very inefficient and mysterious. The greatest cost in running programs tends to be the DOM, not JavaScript. At the Velocity 2008 conference, the Microsoft Internet Explorer 8 team shared this performance data on how time is spent in the Alexa 100 pages.[2]



















The cost of running JavaScript is insignificant compared to the other things that the browser spends time on. The Microsoft team also gave an example of a more aggressive Ajax application, the opening of an email thread.



















The cost of the script is still less than 15%. Now CSS processing is the greatest cost. Understanding the mysteries of the DOM and working to suppress its impact is clearly a better strategy than attempting to speed up the script. If you could heroically make the script run twice as fast, it would barely be noticed.


There is a tendency among application designers to add wow features to Ajax applications. These are intended to invoke a reaction such as, “Wow, I didn’t know browsers could do that.” When used badly, wow features can interfere with the productivity of users by distracting them or forcing them to wait for animated sequences to play out. Misused wow features can also cause unnecessary DOM manipulations, which can come with a surprisingly high cost.

Wow features should be used only when they genuinely improve the experience of the user. They should not be used to show off or to compensate for deficiencies in functionality or usability.

Design for things that the browser can do well. For example, viewing a database as an infinitely scrolling list requires that the browser hold on to and display a much larger set than it can manage efficiently. A better alternative is to have a very effective paginating display with no scrolling at all. This provides better performance and can be easier to use.


Most JavaScript engines were optimized for quick time to market, not performance, so it is natural to assume that JavaScript is always the bottleneck. Typically, however, the bottleneck is not JavaScript, but the DOM, so fiddling with scripts will have little effectiveness.

Fiddling should be avoided. Programs should be coded for correctness and clarity. Fiddling tends to work against clarity, which can increase the susceptibility of the program to attract bugs.

Fortunately, competitive pressure is forcing the browser makers to improve the efficiency of their JavaScript engines. These improvements will enable new classes of applications in the browser.

Avoid obscure idioms that might be faster unless you can prove that they will have a noticeable impact on your application. In most cases, they will have no noticeable impact except to degrade the quality of your code. Do not tune to the quirks of particular browsers. The browsers are still in development and may ultimately favor better coding practices.

If you feel you must fiddle, measure first. Our intuitions of the true costs of a program are usually wrong. Only by measuring can you have confidence that you are having a positive effect on performance.


Everything is a trade-off. When optimizing for performance, do not waste time trying to speed up code that does not consume a significant amount of the time. Measure first. Back out of any optimization that does not provide an enjoyable benefit.

Browsers tend to spend little time running JavaScript. Most of their time is spent in the DOM. Ask your browser maker to provide better performance measurement tools.

Code for quality. Clean, legible, well-organized code is easier to get right, easier to maintain, and easier to optimize. Avoid tricks except when they can be proven to substantially improve performance.

Ajax techniques, when used well, can make applications faster. The key is in establishing a balance between the browser and the server. Ajax provides an effective alternative to page replacement, turning the browser into a powerful application platform, but your success is not guaranteed. The browser is a challenging platform and your intuitions about performance are not reliable. The chapters that follow will help you understand how to make even faster web sites.

The best content for your career. Discover unlimited learning on demand for around $1/day.