Duff's Device for JavaScript

Nicholas Zakas in his book "High Performance JavaScript" has mentioned about Jeff Greenberg's JavaScript implementation of Tom Duff's original technique of unrolling loop bodies in order to let each iteration perform job of many iterations.

for (var i = 0, loopLen = items.length; i < loopLen; i++) {
    process(items[i]);
}

can be rewritten with this technique as:

var arrLen = items.length;
var loopLen = Math.floor(arrLen / 8),
    startAt = arrLen % 8,
    i = 0;

do {
    switch (startAt) {
        case 0: process(items[i++]);
        case 7: process(items[i++]);
        case 6: process(items[i++]);
        case 5: process(items[i++]);
        case 4: process(items[i++]);
        case 3: process(items[i++]);
        case 2: process(items[i++]);
        case 1: process(items[i++]);
    }

    startAt = 0;
} while (--loopLen)

Suppose, the "items" array contains 10 items. The first iteration will call "process()" 2 times - 'case 2:' and 'case 1:'. (Note that the break statement has been excluded intentionally). The second iteration will invoke "process()" 8 times. This has reduced the number of iterations from 10 to just 2.

Jeff Greenberg has even improved this pattern further by having a separate loop construct for the first part of the iterations and totally getting rid of the 'switch' statement thereon.

var arrLen = items.length;
var i = arrLen % 8;

while (i) {
    process(items[i--]);
}

i = Math.floor(arrLen / 8);

while (i) {
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
    process(items[i--]);
}

Though for smaller number of iterations there isn't a significant gain on performance, it is seen that, at 500,000 iterations the execution time is up to 70% less than a regular loop.

30 best practices to boost your web application performance

  1. Limit interaction with DOM as much as possible. Store DOM references in local JavaScript variables before using them if you need them more than once. Consider setting innerHTML over document.createElement/appendChild().
  2. eval() is evil. So is the constructor new Fuction(). Avoid them to the maximum extent.
  3. Say no to with statement. It introduces an extra scope to search each time variable is referenced and the contents of that scope is not known at compile time.
  4. Consider for() over for-in loop. The for-in loop requires the script engine to build a list of all the enumerable properties and check for duplicates prior the start.
  5. Instead of putting try-catch inside a loop, put the loop inside a try-catch. Exception handling should be done at as high level in the script where it does not occur frequently.
  6. Even the holy Bible mentions this - NO Globals. Variable in the global scope persist though the life time of the script, whereas in local scope they are destroyed when the local scope is lost. Also when a global variable is referenced inside a function or another scope, the script engine has to search through the whole scope to find it.
  7. fullName += 'John'; fullName += 'Holdings'; is faster than fullName += 'John' + 'Holdings';
  8. But if you have multiple string concatenations, prefer pushing to an Array and then calling the join() method on it. This is particularly helpful while building HTML snippets.
  9. For simple tasks, prefer Primitive operations over function calls e.g. val1 < val2 ? val1 : val2; is faster than Math.min(val1, val2); and similarly myArr.push(newEle); is slower than myArr[myArr.length] = newEle;
  10. A function reference perform better than (hard coded) string literals as parameters to setTimeout() and setInterval(). E.g. setTimeout("someFunc()", 1000) will perform slower than setTimeout(someFunc, 1000)
  11. Avoid DOM modifications while traversing. The DOM element lists returned by functions like getElementsByTagName() are live; so changing these collections won't wait for execution to finish first. This might also cause an infinite loop to occur.
  12. Store references to object members (properties and even methods) if you are going to need it more than once e.g. var getTags = document.getElementsByTagName; getTags('div');
  13. Store local references to out-of-scope variables to provide quicker access and avoid lookup. E.g.
        var a = 'something';
     
        function foo() {
            //create a local reference for 'a', which is out-of-scope of this function
            var l = a;
            
            //do something with l
        }
  14. for(var i=0; i < someArray.length; i++) {...} is slower than for (var i=0, loopLen=someArray.length; i<loopLen; i++) {...}.
  15. Add Expires and max-age directive of Cache-Control HTTP Header in server responses.
  16. Optimize CSS. Prefer <link> over @import. Refer to this beautiful presentation http://www.slideshare.net/stubbornella/object-oriented-css
  17. Use CSS sprites to cut down on image resources
  18. GZip .js and .css files. On Apache if you set something like this in .htaccess, it will also gzip your HTML, XML and JSON
    AddOutputFilterByType DEFLATE text/html text/css text/plain text/xml application/x-javascript application/json
  19. Use JavaScript minifier. Apart from YUI and JSMin, also try Google Closure http://closure-compiler.appspot.com/home (Courtesy: James Westgate, a fellow reader)
  20. Optimize each and total number of resources for every view and split them across sub-domains to enable parallel downloads. For more information on parallel downloads, read this http://yuiblog.com/blog/2007/04/11/performance-research-part-4/
  21. Put stylesheets at the top to help progressive rendering on many browsers including IE
  22. Try to keep your DOM structure as light as possible. The size of DOM slows down all the operations related to it such as reflowing, traversal and DOM manipulation. document.getElementsByTagName('*').length should return as small value as possible.
  23. Pay attention to the selectors you use. E.g. if you are trying to collect the list items which are direct children of an unordered list, use jQuery("ul > li") rather than jQuery("ul li")
  24. While toggling element visibility (display) remember this: element.css({display:none}) is faster than element.hide() or element.addClass('myHiddenClass'). But unless you are using it inside a loop, I vote for element.addClass('myHiddenClass') because it keeps your HTML clean - remember that thumb rule from my earlier article? No inline CSS or JavaScript.
  25. When you are done with variable references to DOM, nullify them. JavaScript leaves that power with you.
  26. With AJAX, GET works faster than POST. So prefer GET wherever you can. Just keep in mind IE restricts the GET request length to 2K.
  27. Go easy on scripted animations. Animation without hardware support is slow. Try to avoid excessive use of animations which do not bring any real usability value.
  28. Avoid background-repeat if the background-image is small relative to its containing element. Interaction such as scrolling is extremely slow if background-repeat property is set to repeat-x or repeat-y combined with a background-image of a size which needs to be repeated multiple times to fill in the element's background. Try a commonly used CSS pattern to have a big enough background-image with background-repeat: no-repeat.
  29. Do not use <table>s for layout. <table>s often require multiple passes before the layout is completely established because they are one of the rare cases where elements can affect the display of other elements that came before them on the DOM. And for data tables, use table-layout:fixed; this will allow a more efficient layout algorithm and let the table render row by row according to the CSS 2.1 specification.
  30. Always fall back to core JavaScript whenever possible. Limit framework usage.

Lazy loading of images across different browsers

A colleague pinged me today over an IE6 issue where she saw background images on elements with style 'display:none' are getting loaded on IE6 on page load but not on Firefox.

My first take was, this shouldn't be an issue at all. I mean, browser developers should have this freedom to decide whether they want to load these images upfront or on-demand. As an UI developer, if I want to have lazy loading on particular elements, especially images, I should handle that explicitly.

I could check with Chrome and found that it also loads those 'display:none' images upfront. So, absolutely nothing wrong there.