touchingdom (v4)

Revision 4 of this benchmark created on


Description

This is a simple benchmark demonstrating how expensive it is to access the DOM directly. Doing something simple as reading "element.childNodes" adds a significant amount of overhead to browser-based apps.

Template libraries such as Glimmer, React, and Paperclip implement a sorta virtual DOM that abstracts DOM manipulations for you so that nodes are touched as little as possible. The primary difference between React, and other libraries is that React uses a virtual representation of the DOM in javascript, and applies diffs from that virtual DOM to real nodes (the slow stuff). Glimmer, and Paperclip on the other hand take raw HTML and compile it to an immutable DOM template which gets cloned whenever it's needed in a view. Since templates in Glimmer and Paperclip are immutable, all optimizations happen at compile time - no diffing, just changing the dynamic elements it needs to with the fewest DOM manipulations possible.

Setup

var html = 
    "<div> \
      <div> \
        <span>a</span> \
        <span>b</span> \
        <span>c</span> \
        <span>d</span> \
      </div> \
      <div> \
        <span>a</span> \
        <span>b</span> \
        <span>c</span> \
        <span>d</span> \
      </div> \
      <div> \
        <span>a</span> \
        <span>b</span> \
        <span>c</span> \
        <span>d</span> \
      </div> \
      <div> \
        <span>a</span> \
        <span>b</span> \
        <span>c</span> \
        <span>d</span> \
      </div> \
    </div>".replace(/[\s\r\n\t]+/g,"");
    
    var div = document.createElement("div");
    div.innerHTML = html;
    
    function toVDOM(div) {
    
      var type = div.nodeType;
      
      if (type === 1) {
        return {
          nodeType: type,
          nodeName: div.nodeName,
          childNodes: Array.prototype.map.call(div.childNodes, toVDOM)
        };
      } else if (type === 3) {
        return {
          nodeType: type,
          nodeValue: div.nodeValue,
          childNodes: []
        };
      } else {
        return {
          nodeType: type
        };
      }
    }
    
    var vdiv = toVDOM(div);
    
    function walk(node) {
      if (node.nodeType === 1 && node.childNodes.length)
      for (var i = node.childNodes.length; i--;) walk(node.childNodes[i]);
    }
    
    function walkDOM(node) {
      if (node.nodeType === 1) {
          var current = node.firstChild;
          while (current) {
                  walkDOM(current);
                  current = current.nextSibling;
          }
      }
    }

Test runner

Ready to run.

Testing in
TestOps/sec
dom touch 1
div.firstChild;
 
ready
dom touch 2
div.firstChild.firstChild;
ready
dom touch 3
div.firstChild.firstChild.firstChild;
ready
dom walk
walkDOM(div);
ready
vdom touch 1
vdiv.childNodes[0];
ready
vdom touch 2
vdiv.childNodes[0].childNodes[0];
ready
vdom touch 3
vdiv.childNodes[0].childNodes[0].childNodes[0];
ready
vdom walk
walk(vdiv);
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.