Thursday, May 10, 2012

Comparing the performance of $("#foo .bar”) and $(".bar”, "#foo”)


Scroll down for the getById.getByClassName vs. qSA comparison!





If we wanted to select all elements of class "bar" which are inside the element with the ID "foo" , we could write this:




$( '#foo .bar' )



or this:




$( '.bar', '#foo' )



There are of course other methods to achieve this, but for the sake of this question, let's compare only these two methods.



So, which of the above methods performs better? (Which needs less time to execute?)



I have written this performance test:




(function() {
var i;

console.time('test1');
for( i = 0; i < 100; i++ ) {
$('#question-mini-list .tags');
}
console.timeEnd('test1');

console.time('test2');
for( i = 0; i < 100; i++ ) {
$('.tags', '#question-mini-list');
}
console.timeEnd('test2');
})();



You have to execute it from within the console on the Stack Overflow start-page . My results are:



Firefox:

test1: ~90ms

test2: ~18ms



Chrome:

test1: ~65ms

test2: ~30ms



Opera:

test1: ~50ms

test2: ~100ms



So in Firefox and Chrome, the second method is multiple times faster - just as I expected. However, in Opera the situation is reversed. I wonder what's going on here.



Could you please run the test on your machine and explain why Opera performs differently?





Update



I've written this test, in order to investigate whether Opera's qSA really is super-fast. As it turns out, it is.




(function() {
var i, limit = 5000, test1 = 'test1', test2 = 'test2';

console.time( test1 );
for( i = 0; i < limit; i += 1 ) {
document.getElementById( 'question-mini-list' ).getElementsByClassName( 'tags' );
}
console.timeEnd( test1 );

console.time( test2 );
for( i = 0; i < limit; i += 1 ) {
document.querySelectorAll( '#question-mini-list .tags' );
}
console.timeEnd( test2 );
})();



Again, you have to run this code from within the console on the Stack Overflow start-page. I used the Firebug Lite bookmarklet for IE9 (since that browser doesn't implement console.time ).



So, I compared this method:




document.getelementById( 'A' ).getElementsByClassName( 'B' );



to this method:




document.querySelectorAll( '#A .B' );



I've executed the above script five consecutive times in each browser. The arithmetic means are:



enter image description here



(All numbers are in milliseconds.)



So, the performance of the first method is pretty much the same in the tested browsers (16-36ms). However, while qSA is much slower compared to the first method, in Opera it actually is faster!



So, qSA optimization is possible, I wonder what the other browsers are waiting for...


Source: Tips4all

3 comments:

  1. jQuery/Sizzle will avoid using the JavaScript based Sizzle engine if the browser supports querySelectorAll, and if you pass a valid selector (no custom, non-CSS selectors).

    This means that you're ultimately comparing implementations of querySelectorAll, assuming you're testing browsers that support it.

    There are other optimizations that jQuery or Sizzle uses, so it's tricky when comparing different types of DOM selection in different browsers.

    The reason for Opera's performance result seems to be that they have a very highly optimized querySelectorAll implementation. qSA, being a relatively new method, hasn't been quite as optimized in some browsers compared to older methods like getElementsByTagName.

    ReplyDelete
  2. For reference, this is 30x faster:

    document.getElementById("foo").getElementsByClassName("bar");


    See jsPerf: http://jsperf.com/jquery-selector-variations/3. This would need a shim to work in older versions of IE.

    While jQuery is extremely useful, if speed is of the utmost, it's not always the best tool for the job.

    ReplyDelete
  3. And the winner is....

    test 3 $('#question-mini-list').find('.tags');


    test1: 25ms
    test2: 19ms
    test3: 10ms


    The two methods you suggested are not equivalent.

    test 1: Sizzle parses from right to left (don't ask it to search ever element on the page, then restrict to an ID).

    test 2: Using a string as a context is generally of no use, use elements as a context.

    test 3: Finding elements with an id is blazingly fast. Once you're there it's a breeze to focus in on an item of a given class.

    ReplyDelete