Subscribe to our mailing list

* indicates required
Close

Monday, April 27, 2009

Two techniques for faster JavaScript

I like things that go fast, and that includes code that runs fast. With JavaScript (and Java, too), that can be a challenge. So much the better, though. I like challenges too.

When someone asks me what's the single best way to speed up "a slow script," naturally I want to know what the script is spending most of its time doing. In browser scripting, it's typical that a "slow" script operation either involves tedious string parsing of some kind, or DOM operations. That's if you don't count programmer-insanity sorts of things, like creating a regular expression object over and over again in a loop.

The two most important pieces of advice I can give on speeding up browser scripts, then, are:

1. Never hand-parse a string.
2. Don't do DOM operations in loops (and in general, don't do DOM operations!).

No. 1 means don't do things like crawl a big long string using indexOf( ) to tokenize-as-you-go. Instead, use replace( ) or a split( )/join( ) technique, or some other technique that will basically have the effect of moving the loop into a C++ native routine inside the interpreter. (The general approach is discussed in a previous post.) An example would be hit-highlighting in a long run of text. Don't step through the text looking for the term(s) in question; use replace( ).

No. 2 means to avoid looping over the return values from getElementsByTagName( ) -- in fact, don't call it unless you have to -- and get away from doing a lot of createElement( ), appendChild( ) types of things, especially in loops, and especially in functions that get called a lot (such as event handlers for mouse movements). How? Use innerHTML wherever possible. In other words, create your "nodes" as Strings (markup), then slam the final string into the DOM at the last minute by setting the parent node's innerHTML to that value. This moves all the DOM reconfiguring into the browser's native DOM routines, which it'll happen at the speed of compiled C++. Don't sit there and rebuild the DOM yourself, brick by brick, in JavaScript, unless you have to, which you seldom do.

There are other techniques for avoiding big speedups, but they're more situational. And I'm still learning, of course. I'm still trying to find out what all the lazily-invoked "big speed hit" operations are in Gecko that can suddenly be triggered by scripts. The situational speed hits can sometimes be addressed through caching of expensive objects, or reuse of expensive results (a technique known as memoization; good article here). The Mozilla folks have put a lot of work into speeding up the JavaScript runtimes, but remember, the fastest runtime environment in the world can be brought to its knees by poor choice of algorithms.

Obviously it's not always possible to employ the two techniques mentioned above, and in certain cases the performance gain is not impressive. But in general, these remain underutilized techniques (from what I can tell), which is why I bring them up here.

If you have additional techniques for speeding up JavaScript, by all means, leave a comment. I'm interested in hearing your experiences.

15 comments:

  1. If .innerHTML is going to be used to display user input, make sure to santize it first.. Dont be a victim

    ReplyDelete
  2. Also, if you really do need to access elements by a tag name inside a loop, do something such as:

    var my_elements = document.getElementsByTagName(some_element);

    and then refer to my_elements, not the DOM call inside the loop.

    ReplyDelete
  3. Anonymous8:48 AM

    I like to speeden up my jigabooscript with teh buttsechs.

    -Ibod Catooga

    ReplyDelete
  4. I'm sorry, but the "innerHTML" trick has been shown to have no significant speedup over DOM operations in a benchmark. Just Google it.

    ReplyDelete
  5. Use the profiler of Firebug and start optimizing from the top. It's pretty easy to see the bottlenecks. When you reach the much lower level functions being at the top of the profiler, with the differences between them being little and overall being quite fast to what they do, then you have a problem to extract more performance from it. ;-) Luckily more powerful browsers are coming...

    And that's another problem. Given JavaScript's issues can be implementation dependent, it sometimes helps little to try to optimize too much when a new browser which is much faster switch everything around and equalizes matters.

    ReplyDelete
  6. DOM DocumentFragments can actually be faster than innerHTML:

    http://ejohn.org/blog/dom-documentfragments/

    ReplyDelete
  7. @nixar: it does if it eliminates repeated injections into the DOM. One might argue that you can do the same with DOM operations by building a DOM tree and then inserting the whole tree into the document once. However, this technique has been shown to leak memory in IE 6&7.

    ReplyDelete
  8. There's an example of fast parsing in JS at http://code.google.com/p/json-sans-eval/source/browse/trunk/src/json_sans_eval.js which uses a single global regex match to tokenize the input which avoids the need for hand parsing.

    It turns out that consuming a string using a loop like
    while (input) {
    var token = input.match(/^.../);
    ...
    input = input.substring(token.length);
    }
    does O(n^2) work copying char buffers on some interpreters, but
    var tokens = input.match(/^.../g);
    is linear in the length of the string (ignoring the complexity of your regex).

    ReplyDelete
  9. Anonymous2:42 PM

    jQuery

    ReplyDelete
  10. @nixar I googled it, and came up with this, which seems to say to me that innerHTML is much faster than DOM, especially in IE 6 & 7: http://www.quirksmode.org/dom/innerhtml.html

    ReplyDelete
  11. I think you should look into the performance of browsers other than IE -- in Safari at least mutating innerHTML is much slower than creating and appeneding dom elements - which makes sense because the difference between appending a dom element and mutating innerHTML is that mutating innerHTML has to parse and then recreate every DOM node in the original element all over again.

    Likewise performance of DOM NodeLists (eg. the results of getElementsByTagName, etc) varies dramatically between engines, so you should look at there perf in multiple browsers as well.

    ReplyDelete
  12. Before anything...measurement is key! Don't waste cycles speeding up something that is only 0.01% of your total cycle usage.

    ReplyDelete
  13. I recently needed to get every last bit of performance out of some JavaScript code - mostly just pure script processing speed, and not related to DOM or such.

    I found a few nice tricks, which I've documented in my blog. Some of them are, though, the kind of which can make your code less readable.

    http://codeutopia.net/blog/2009/04/30/optimizing-javascript-for-extreme-performance-and-low-memory-consumption/

    ReplyDelete
  14. I haven't really cared about optimizing client-side scripts, but since I've started using nodejs (server-side javascript), I just might consider performance.

    ReplyDelete
  15. Anonymous4:51 PM

    1. Never hand-parse a string. = Never drive yor car off road it might force you go slow.
    2. Don't do DOM operations in loops (and in general, don't do DOM operations!). = Dont take your car on frequent trips back and fourth - go there and stay there instead. ( and in general do not drive your car).

    I. the purpose of using javascript on a brower is to manipulate its content; it is represented by dom.
    II. Dont do dom manipulations equals dont use javascript.
    So forth the script is as fast as it is - what's slowing you down is the DOM, and 'dom api' is not javascript, meaning -you cant fucken use javascript to speed up the dom.
    Thank you.

    ReplyDelete

Add a comment. Registration required because trolls.