Tuesday, 23 October 2012

More on jQuery id selection performance

In response to Ron's comment on the attr('id') vs [0].id post last week asking about the jQuery.fn.prop's vs jQuery.fn.attr's performance, I extended the program to include prop and enabled profiling across all browsers (thanks to time.js). The program also does the tests a little more thoroughly, running 1000000 operations 5 times per each operation and averages the result.

Some of the result surprised me a little, in particular Safari beating Chrome on d.attr('id') but nothing else, and d.prop('id') was the clear winner.


These charts are going to be a lot nicer when we can start using IE10 ;)

Chrome 22

d.attr('id'): 1164ms
d.prop('id'): 947ms
d[0].id: 56ms
d.get(0).id: 64ms
d[0].getAttribute('id'): 95ms

Firefox 16

d.attr('id'): 2787ms
d.prop('id'): 2198ms
d[0].id: 161ms
d.get(0).id: 171ms
d[0].getAttribute('id'): 188ms

IE 9

d.attr('id'): 5051ms
d.prop('id'): 2780ms
d[0].id: 273ms
d.get(0).id: 298ms
d[0].getAttribute('id'): 793ms

Opera 12

d.attr('id'): 1534ms
d.prop('id'): 1377ms
d[0].id: 312ms
d.get(0).id: 324ms
d[0].getAttribute('id'): 354ms

Safari 5.1

d.attr('id'): 1082ms
d.prop('id'): 997ms
d[0].id: 140ms
d.get(0).id: 155ms
d[0].getAttribute('id'): 190ms


HTML

<html>
<head>
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
  <script src="http://remysharp.com/time.packed.js"></script>
  <script>
    var numberOfTypeTests = 5;
    var numberOfIdTests = 1000000;
    var div;
    
    $().ready(function () {
      $('#run').click(run);
      $('#report').click(function () { time.report(); });
      div = $('#test');
      time.setReportMethod(reportMethod);
    });

    function run() {
      for (var i = 0; i < numberOfTypeTests; i++) { testAttr(); }
      for (var i = 0; i < numberOfTypeTests; i++) { testProp(); }
      for (var i = 0; i < numberOfTypeTests; i++) { testConvert(); }
      for (var i = 0; i < numberOfTypeTests; i++) { testGet(); }
      for (var i = 0; i < numberOfTypeTests; i++) { testGetAttribute(); }
      time.report();
    }
    
    function testAttr() {
      test('d.attr(\'id\')', function (d) { d.attr('id'); });
    }
    
    function testProp() {
      test('d.prop(\'id\')', function (d) { d.prop('id'); });
    }
    
    function testConvert() {
      test('d[0].id', function (d) { d[0].id; });
    }
    
    function testGet() {
      test('d.get(0).id', function (d) { d.get(0).id; });
    }
    
    function testGetAttribute() {
      test('d[0].getAttribute(\'id\')', function (d) { d[0].getAttribute('id'); });
    }
  
    function test(name, f) {
      time.start(name);
      for (var i = 0; i < numberOfIdTests; i++) { f(div); }
      time.stop(name);
    }
  
  function reportMethod(t) {
    var report = '';
    for (var i = 0; i < t.length / numberOfTypeTests ; i++) { // Type test
      var total = 0.0;
      for (var j = 0; j < numberOfTypeTests; j++) {
        total += t[i * numberOfTypeTests + j].delta;
      }
      var avg = Math.round(total / numberOfTypeTests);
      report += t[i * numberOfTypeTests].name + ': ' + avg + 'ms<br />';
    }
    $('#output').html(report);
  }
  </script>
</head>
<body>
  <button id="run">Run tests</button>
  <div id="test"></div>
  <div id="output"></div>
</body>
</html>