Frontend Performance Tuning

Premature optimization is the root of all evil.
High Performance Web Sites: Essential Knowledge for Frontend Engineers
Cuzillion (web performance tool like YSlow)
YUI Blog
Google and Microsoft Cheat on Slow-Start. Should You?
High Performance Web Sites and YSlow
Life's Too Short - Write Fast Code (part 2)

Even Faster Websites
Cuzillion (web performance tool like YSlow)
Steve Souders: High Performance Web Sites: 14 Rules for Faster Pages
Gopal Venkatesan: Writing Efficient JavaScript
Julien Lecomte: High Performance Ajax Applications
Nicholas Zakas: Maintainable JavaScript
Joseph Smarr: High-performance JavaScript: Why Everything You've Been Taught is Wrong

  1. Make fewer HTTP requests
    1. CSS Sprites
    2. Combine and minify scripts. Minify the combined JS file. You can also minify your inline JS as well. On the server side, what ever language you use to generate the content, you can have a function to minify your inline javascript.
    3. combine and minify stylesheets
  2. Use a CDN (Content Delivery Network). Distribute your static contents. CDN providers: Akamai, Mirror Image, SAVVIS, LimeLight. Pick one that can distribute your static contents using GEO-IP. Distribute your static contents before distributing your dynamic contents (having multiple data centers, or bring your dynamic content servers and databases closer to the visitor). Know where your visitors are coming from before deciding on where to put your dynamic content servers and databases.
  3. Maximize the use of browser cache by adding appropriate cache control directive
  4. Gzip components. Gzip html, scripts, stylesheets, XML, but not binary content (images, PDFs)
  5. Put stylesheets at the top
  6. Use LINK tag instead of @import
  7. Move scripts to the bottom. Scripts block the rendering of anything below them in the page. Scripts also block parallel downloads (images and stylesheets below the script) across all hostnames. In HTTP/1.1, browsers can download 2 components in parallel per hostname.
  8. Avoid CSS expression. See below.
  9. Make CSS and JS external. For complex home page, inline CSS and JS, but as soon as the page is done loading, you can preload external CSS and JS. As the server serve the homepage, it can set a cookie. The next time the visitor hit the home page, the server see the cookie, a strong indication that those CSS and JS had been preloaded, so the server can serve the homepage without inlining those CSS and JS files.
  10. Reduce DNS lookups. Don't need to do anything here. Operating systems cache result of DNS lookup already.

What is CSS expression?

IE supports something call CSS expression. A lot of time, people use it to address browser incompatibilities across browsers. An expression is just a javascript expression that IE executes and the result of that expression is set as css style.

To deal with the lack of minWidth:

width: expression(document.body.clientWidth < 600 ? "600px" : "auto");

The problem: CSS expression are executed many more times than you expect. They are executed everytime you move the mouse around the page, press a key, resize the page, scroll, etc.


  1. use one-time expression (an expression that remove itself)
  2. use an event handler

CSS expressions are tied to the events automatically so you don't have to worry about that, but that is what is bad about it. It is tied to all the events, so figure out what event you need this behavior, and write an event handler for that.

Avoid redirects

  1. adds additional delay, and are not cached unless you add appropriate cache control headers.
  2. worst form of blocking

Remove duplicate javascripts

Configure ETag

The problem with most common setup for ETag is that it use inode (which is different across servers), and timestamp (might not be reliable across servers). The only thing you can count on being the same across servers is the size of the file, and its content (otherwise your push script is broken). It may be nice if we can md5 the content (might add overhead), so the only thing we might be able to count on is the file size, which also has issue (if the number of characters remain the same). Turn off ETag:

FileETag none

The use of ETags in the HTTP header is optional (not mandatory as with some other fields of the HTTP 1.1 header). The method by which ETags are generated has never been specified in the HTTP specification. Common methods of ETag generation include using a collision-resistant hash function of the resource's content, a hash of the last modification timestamp, or even just a revision number. In order to avoid the use of stale cache data, methods used to generate ETags should guarantee (as much as is practical) that each ETag is unique. However, an ETag-generation function could be judged to be "usable" if it can be proven (mathematically) that duplication of ETags would be "acceptably rare", even if it could or would occur. Some earlier checksum functions, such as CRC32 and CRC64, are known to suffer from this hash collision problem. Because of this they are not good candidates for use in ETag generation.

Strong ETags permit the caching and reassembly of partial responses, as with byte-range requests.

When a URL is retrieved, the web server will return the resource's current representation along with its corresponding ETag value, which is placed in an HTTP response header "ETag" field. The client may then decide to cache the representation, along with its ETag. Later, if the client wants to retrieve the same URL again, it will send its previously saved copy of the ETag along with the request in a "If-None-Match" field.

The server may now compare the client's ETag with the ETag for the current version of the resource. If the ETag values match, meaning that the resource has not changed, then the server may send back a very short response with a HTTP 304 Not Modified status. The 304 status tells the client that its cached version is still good and that it should use that. However, if the ETag values do not match, meaning the resource has likely changed, then a full response including the resource's content is returned, just as if ETags were not being used. In this case the client may decide to replace its previously cached version with the newly returned representation of the resource and the new ETag.

Some AJAX requests might be cacheable in the browser

AJAX requests are usually dynamic, and personal, therefore give a false sense that it cannot be cache, but in reality, some AJAX requests are cacheable. Imagine an email application with a contact list, and the user don't update the contact list often. What you want to do is to add the timestamp of the last time the user update the contact list to the URL. When the user update his contact list, you update this timestamp in the user preference table. When you see the AJAX request with the timestamp, you can compare it with the timestamp in the preference table. Using this technique, is it possible that we can have a situation where the server thinks that the browser has the content in the cache, but it actually does not? This is definitely possible, if the user, for some reason, clear the browser cache.

Minimize the use of global variables / namespace - Use local references when possible.

Javascript sequentially search up the stack chain for variable references therefore local variables will be found first. Speed up code by creating a local variable pointing to global variable (if there need to be a global variable at all).

Variables and functions in global scope exist through the life cycle of the script, memory deallocation can only be done at the end of execution.

Cache frequently used objects and properties.

for (var i=0, len = myArray.length; i < len; i++)

is better than

for (var i=0; i < myArray.length; i++)
var s = document.getElementById('myobject').style;

is better than


Cache function pointers.

If you need to call a global function repeatedly inside a loop, assign the function reference to a local variable:

function A() {}
function B() 
    var x = A;
    for (var i = 0; i < 100; i++)

is better than:
function A() {}
function B()
    for (var i = 0; i < 100; i++)

Assigning the function reference to a local variable only make sense when this is happening inside an outer function.

Avoid using eval when possible.

Avoid the Function constructor.

var callback = new Function("...");

Using the Function constructor is equivalent to using eval since the string inside the constructor needs to be evaluated.
var callback = function() { ... };

is better than:
var callback = new Function("...");

Avoid the 'with' statement


with (
    color = '#fff';
    foo = something;

The use of 'with' creates an extra scope, and the contents of the scope are not known ahead of time for optimization. JavaScript need to understand whether color is a global variable or it is a property of In this example, foo is not a property of, a global variable is created. What is the scope of something? Is it a global variable, a local variable, a property of ?
var s =;
s.color = '#fff';

is better than using the 'with' statement.

Avoid using try/catch inside a loop.

The try/catch block creates a local scope and create an exception object at runtime that live in that scope.

try {
    for(...) {
} catch(e) { ... }

is better than:
for(...) {
    try {
    } catch(e) { ... }

Minimize DOM insertion.

Browser will re-evaluate the page on each change to the DOM. Keep new element insertions to a minimum. When you do modify the DOM, it is best to lump all the insertion together.

Global variables and functions are indexed by named. Local variables are kept on stack.

Random stuff.


is better than:
setTimeout('myFunc()',...);  // eval() is used here.

Either Array.join() or String.concat() is better than '+' when concating a lot of strings.

var s = ["hello", " world"].join("");
var s = String.concat("hello", " world");

is better than
var s = "hello" + " world";

Hide the element (if possible) whenever you need to modify a lot of properties.

Everytime you change a property, the browser has to re-render the document.

This is another recommended way:

// This technique need improvement because we need to find out what the current style is
var d = document.getElementById("...");
var style = "background: #fff; color: #000; display: block";
if (typeof( != undefined)
{ = style;
} else {

innerHTML is 75% faster than using DOM (maybe)

Dumping HTML into innerHTML is more than 75% faster than using DOM methods especially when adding a lot of elements. It would be interesting to try adding all the elements to a div, and then append the div to the element. (Using innerHTML is definitely more convinient.).

Having minimal re-flow

Some properties like offsetWidth and offsetHeight has internal re-flow when accessed, so cache the property (if you need to access it multiple times):

var d = document.getElementByID('some-ele');
var ow = d.offsetWidth;
// use ow from now on

Minimize the number of event handlers

If you have a group of similar elements, rather than attaching event handler to each element, attach event handler to the container.

Increase parallelism through sub-domains

Browsers allow 2 simultaneous connections per sub-domain.

Be lazy (write minimal amount of code)

  1. If your application is slow, it is because your code is doing stuff that is slow. The less you have to do, the better. If you can get away with not doing something now, the worst case is you have to do it later, but you may not have to do it ever at all.
  2. It is very hard to get a lot of code and make it run really fast.
  3. You pay for every line of code you write. You have to pay for the initial download time. After that, you have to pay in term of parsing, compiling and execution time. Parsing javascript is often a major bottleneck. Browsers do not have JIT, cached object code. Browser re-interpret the code everytime. Parse time is non-linear in the size of total javascript. Ideal size for large AJAX applications is less than 500K of uncompressed JS.

Use a minifier

Use a minifier. Your build system should combine JS files, remove all debug code (less parsing, compiling, and execution time), and minify the combine javascript file.

Load javascript on-demand

Break into classes and modules. Bundle modules into packages. Load the code as needed. Library like Dojo has a system that will check if the module has been loaded, if not it will make an XMLHTTP request, and eval the code.

Draw less DOM

If we have a large DOM fragment (the content of a tab) that is hidden, it is better to remove it from the DOM, and recreate it from the server when the tab is activate. Never attach hidden UI to the DOM if you can avoid it. This makes the DOM less saturated (consume less memory)

Cache previously drawn HTML when appropriate.

Don't keep hidden UI up todate behind the scene - just redraw the next time you show it.

Be responsive

  1. the loading message
  2. put CSS at the top, javascript at the bottom
  3. yield via setTimeout(0), use closure to maintain state across invocation
  4. use onmousedown instead of onclick (100ms difference)
  5. Just redraw (get it from the server). The amount of code that you write to avoid getting data from the server will cause the browser to slow down (parsing, compiling time)

Be pragmatic (knows when you can break the rules)

  1. avoid dynamic CSS class definition and CSS math
  2. avoid memory allocation (i.e. splitting strings)
  3. do DOM manipulation off-DOM and then reinsert at the end (avoid reflow)
  4. directly attach onclick, etc handlers instead of using event handlers where appropriate. Why give it an ID, findElementById and attach an event handler? Just use a global function (perhaps with namespace). Memory leak consideration???
  5. play to your browser strength
  6. know when you can / should not follow stuff that you've been taught.

Be vigilent

Profile early and often. Use YSlow

At my current company, we have a script that combine and minify javascript files. After editing the unminified file, we run the minification script, test, and check both the raw and minified files into source control. The push script push the minified file. This seems cumbersome but has its advantages (we actually test the minified file, the code that do minification only exist in one place). Joseph Smarr use an error handler, in dev environment, to detect that the minified version does not exist, dynamically combines javascript files and minifies the result file. If we use Joseph's approach, we need to make sure that the error handler and the build script use exactly the same code to combine and minify JS.

Object detection is better than browser detection.

Instead of

if (navigator.appName == 'Netscape')


if (obj.offsetHeight)

Google Group for ShowSlow
ShowSlow Blog
Make Your Site Faster With Google Page Speed Online
Improve Browser Performance With the CSS Stress Test Tool
Google PageRank PHP Class
HTTP Archive: max-age launches WPT with mobile devices
Storager case study: Bing, Google
Separating JavaScript download and execution
JavaScript Trie Performance Analysis
Jdrop – JSON in the cloud
ControlJS part 1: async loading
ControlJS part 2: delayed execution
ControlJS part 3: overriding document.write
Docsource mobile bookmarklet
stackJS, a javascript module loader and dependencies handler
Evolution of Script Loading
Lazy Loading Video To Speed Up Your Web Page

  • Premature optimization is the root cause of all evil
  • "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root cause of all evil. Yet we should not pass up our opportunities in that critical 3%." - Donald Knuth, paraphrasing C.A.R Hoarse
  • Cut, cut, cut. Remove unused javascript code. Remove unused style rules. Reduce the number of DOM elements.
  • Understand best practices, and when to use them.
  • Benchmark, benchmark, benchmark
  • Keep it simple.
  • Write just enough code to do the job.
  • Just-in-time development. Only write code when the spec is well defined (has been discussed by the team with input from consumers, and we are absolutely sure that this is what we need). Never develop a feature without validating its concepts / benefits with your team / consumers. Never move on with new feature development until the current feature development is well tested.
  • Use far future caching. If your server is Apache, use the ExpiresDefault directive to set an expiration date relative to the current date. This example of the ExpiresDefault directive sets the Expires date 1 year out from the time of the request. ExpiresDefault "access plus 1 year". Many developers use a build process to add a version number or timestamp to their assets. Each subsequent build will start a brand new cached version, allowing you to use far future cache dates without worry.
  • Use CSS Sprite ( CSS Sprites: What They Are, Why They’re Cool, and How To Use Them, To Sprite Or Not To Sprite)
  • Optimize images. YSlow can autosmush all your images for you.
  • Use cookie-less subdomain for static assets
  • Avoid inline <script> blocks. They block rendering
  • CSS should be located in the <head> of the document, Javascript should be right before the </body> tag.
  • Combine, minify, and gzip CSS and JS files
  • Concatenation and minification typically occur during an automated build process
  • Optimize JavaScript execution. During a page load, there is typically a lot of script waiting to execute, but you can prioritize it. This order prioritizes based on user response. Script that changes the visible nature of the page needs to fire absolutely first. This includes any font adjustment, box layout, or IE6 pngfixes. Page content initialization. Page header, topnav and footer initialization. Attaching event handlers. Omniture, Doubleclick, and other 3rd party scripts
  • Avoid iframes. Iframes are the most costly element to add to a given page. They block the page from firing the onload event until they are complete.
Is premature optimization really the root of all evil?
101 on jQuery Selector Performance
Page Speed 1.6 Beta - new rules, native library
Conditional Compilation
Plurk: Instant conversations using Comet
Plurk Comet: Handling of 100.000+ open connections
Fast polling using C, memcached, nginx and libevent
Comet (long polling) for all browsers using ScriptCommunicator
Comet with node.js and V8
HipHop for PHP: Move Fast (Facebook optimization for PHP)

15 Tools to Help You Develop Faster Web Pages
High Performance JavaScript
Browser Performance Wishlist
Make the DOM Update Faster
Redis in Practice: Who’s Online?
A Collection Of Redis Use Cases
Making Facebook 2x Faster
Watching MozAfterPaint in Firebug
Serving Static Content from a Cookieless Domain
101 on HTTPS Web Site Performance Impact
Top Low Hanging Fruit to Performance Optimize your Web Site and boost Business
Ensuring Web Site Performance – Why, What and How to Measure Automated and Accurately
Inability to measure SLAs around application performance
User based service level enforcement for Web Applications
Week 2 – The many faces of end-user experience monitoring
Week 4 – Why “top ten” Performance Reports are not the final answer
Week 3 – Myths and Truths about Performance Measurement Overhead
Week 6 – How to Make Developers Write Performance Tests
Performance Management in Continuous Integration
Continuous Performance Management in Development
Performance Antipatterns – Part 1
Performance Antipatterns and Crowdsourcing
Video on Common Performance Antipatterns online
Performance Antipatterns in AJAX Applications
The Right Architecture: Performance in AJAX Applications
Links to recorded Web/AJAX Performance Webinars
dynaTrace Ajax Edition: tracing JS performance
A Step-by-Step Guide to dynaTrace Ajax Edition, available today for public download
Selected readings for my JAX London Session
Understanding Caching in Hibernate – Part One : The Session Cache
Understanding Caching in Hibernate – Part Two : The Query Cache
Understanding Caching in Hibernate – Part Three : The Second Level Cache
Lazy vs. Eager Loading in Hibernate
JPA Under The Hood – Understanding the Dynamics of Your JPA Framework
Videos from Andreas Grabner Technology Strategist at dynaTrace
Performance Workshop at W-JAX
Best Practices to Diagnose and Prevent AJAX Performance Issues in Complex Web2.0 Applications:
5 Steps to Automate Browser Performance Analysis with Watir and dynaTrace AJAX Edition
Automate Testing and Root-Cause Analysis with PushToTest and dynaTrace
Your Top Links about Web Site/AJAX Performance
Cappuccino is an open source framework that makes it easy to build desktop-caliber applications that run in a web browser
Javascript Best Practices
How to Find Invisible Performance Problems
Do more with Functional Testing – Take the Next Evolutionary Step
Randomizing Input Data for Visual Studio Load Tests
Eating Our Own Dog Food: How dynaTrace does Continuous APM Internally in Development with dynaTrace TA Lead Stefan Frandl
Get more out of functional web testing: How to correlate test reports with server side log information?
Selenium/BrowserMob integration with dynaTrace
Performance vs. Scalability
Week 3 – Myths and Truths about Performance Measurement Overhead
The problem with SLA monitoring in virtualized environments
Proof of Concept: dynaTrace provides Cloud Service Monitoring and Root Cause Analysis for GigaSpaces
Automate Testing and Root-Cause Analysis with PushToTest and dynaTrace
VS2010 Load Testing for Distributed and Heterogeneous Applications powered by dynaTrace
Performance Analysis: Identify GC bottlenecks in distributed heterogeneous environments
Performance Analysis: How to identify “bad” methods messing up the GC
Performance Analysis: How to identify synchronization issues under load?
Identify Performance Bottlenecks in your BizTalk Environment – Part II
Identify Performance Bottlenecks in your BizTalk Environment – Part I
Tracing problems in Project Stonehenge and other heterogeneous systems
Challenges of Monitoring, Tracing and Profiling your Applications running in “The Cloud”
Zappos on Application Performance Management Best Practices
Best Practices from Zappos to deliver WOW Performance
Application Performance Management – Free Webinar with the OVUM Butler Group
Automated Performance Analysis: What’s going on in my ASP.NET or ASP.NET MVC Application?
How to analyze and speed up content rich web sites likes in minutes
Why Web 2.0 requires End-To-End Performance Analysis and How to Do It
Performance Analysis of dynamic JavaScript Menus
101 on jQuery Selector Performance
jQuery Performance Rules
Understanding Internet Explorer Rendering Behaviour
The Real Performance Overhead of CSS Expressions
Mozilla Notes on HTML Reflow
Performance Analysis of dynamic JavaScript Menus
The Real Performance Overhead of CSS Expressions
101 on jQuery Selector Performance
5 Steps to Automate Browser Performance Analysis with Watir and dynaTrace AJAX Edition
How to Speed Up sites like by more than 50% in 5 minutes
How to analyze and speed up content rich web sites likes in minutes

Linux HA
Building Web Services with Amazon
appendChild vs insertBefore
PHP Performance Profiling
Optimizing Your Website For Speed
Frontend SPOF
Reducing HTTP requests with generated data URIs
BigPipe: Pipelining web pages for high performance
The PHP Benchmark
Velocity wrap-up
Velocity: Google Maps API performance
Velocity: Top 5 Mistakes of Massive CSS
Data URIs make CSS sprites obsolete
Diffable: only download the deltas
Velocity: TCP and the Lower Bound of Web Performance
Velocity: Forcing Gzip Compression
Better JavaScript Minification
JavaScript Minification Part II
18 Website Speed and Performance Checking Tools
Compress CSS and JavaScript Using PNGs and Canvas
7 Excellent Website To Test And Compare Website Speed and Page Speed
YUI Theater — Ryan Grove: “Achieving Performance Zen with YUI 3″ (40 min.)

  • avoid eval
  • use local variable to cache function pointer
  • avoid using "new Function". It is just as bad as eval.
  • avoid using "with" statement.
  • use local variables
  • avoid using try / catch inside loops
  • do not use setTimeout with a string
  • Array.join() and String.concat() are better than using + when concatenating a lot of strings. var s = ["hello", "world"].join(" "); var s = String.concat("hello", " world");
  • use local variable to cache element property
  • avoid reflow. Hide the element, modify the properties or change all the styles in one shot by getting all the styles into a string, and then assign that string to the style attribute, or use setAttribute.
  • innerHTML is faster than DOM methods, or use the DOM methods to create all of the elements behind the scene, and then plugs all the nodes into the DOM at once.
  • cloneNode is faster than createElement.
  • Some properties like offsetWidth, offsetHeight has internal reflows when accessed. Cache the property and reuse it for your calculations.
  • If we are appending a lot of elements to the DOM, detach the node, append the children, and re-attach the node.
  • When attaching the same event handler to a lot of elements in the page, it is best to attach the event handler to the container.

Know when to use classes vs IDs, and performance consideration for CSS. (See Google Page Speed)
Descendant selectors vs using classes (See Google Page Speed and
Always use CSS Sprites for rollovers.
Make your page responsive. Link should change color when you mouseover.

Things to consider as we code:

  • performance
  • maintainability
  • pragmatic

On the main page, load additional javascript/css file when the page finish loading and set a cookie. On the server side, check to see if the cookie is sent. If so, do not include the additional javascript / css files into the page.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License