Hadlebars vs. Hogan vs. Mustache

JavaScript performance comparison

Revision 4 of this test case created by danielbushman

Info

Illustrate how much time string-based template engines use executing browsers HTML parser.

Optimizing string interpolation performance is focusing to 10% of the problem, not the 90%.

Preparation code

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>

<script src="http://github.com/janl/mustache.js/raw/master/mustache.js"></script>

<script src="https://github.com/downloads/paul/handlebars.js/handlebars.js"></script>

<script src="//twitter.github.com/hogan.js/builds/1.0.5/hogan-1.0.5.js"></script>

<script src="https://raw.github.com/mgutz/doT/master/doT.runtime.min.js">
</script>

<script>
  window.mustacheTemplate = "<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#list}}<li class='item'>{{.}}</li>{{/list}}</ul></div>";

  window.handlebarsTemplate = Handlebars.compile("<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#list}}<li class='item'>{{this}}</li>{{/list}}</ul></div>");

  window.hoganTemplate = Hogan.compile("<div><h1 class='header'>{{header}}</h1><h2 class='header2'>{{header2}}</h2><h3 class='header3'>{{header3}}</h3><h4 class='header4'>{{header4}}</h4><h5 class='header5'>{{header5}}</h5><h6 class='header6'>{{header6}}</h6><ul class='list'>{{#list}}<li class='item'>{{.}}</li>{{/list}}</ul></div>");

window.dotTemplate = function(it) {
var out = "<div>\n<h1 class='header'>";

out += __dotEncodeHTML(it.header);

out += "</h1>\n<h2 class='header2'>";

out += __dotEncodeHTML(it.header2);

out += "</h2>\n<h3 class='header3'>";

out += __dotEncodeHTML(it.header3);

out += "</h3>\n<h4 class='header4'>";

out += __dotEncodeHTML(it.header4);

out += "</h4>\n<h5 class='header5'>";

out += __dotEncodeHTML(it.header5);

out += "</h5>\n<h6 class='header6'>";

out += __dotEncodeHTML(it.header6);

out += "</h6>\n<ul class='list'>\n<li style=\"list-style: none\">";

var arr1 = it.list;

if (arr1) {
  var item, i1 = -1, l1 = arr1.length - 1;
  while (i1 < l1) {
    item = arr1[i1 += 1];
    out += "</li>\n<li class='item'>";
    out += __dotEncodeHTML(item);
    out += '</li>\n<li style="list-style: none">';
  }
}

out += "</li>\n</ul>\n</div>\n\n";
return out;
}

DOM = {
    createElement : function(type, parent, attributes, properties) {
        var UNDEFINED,
            key, evt,
            element = type ? document.createElement(type) : document.createDocumentFragment();
        if (attributes) {
            for ( var attr in attributes ) {
                if ( attributes.hasOwnProperty(attr) && attributes[attr] !== UNDEFINED && attributes[attr] !== null) {
                    element.setAttribute(attr, attributes[attr]);
                }
            }
        }
        if (properties) {
            for ( var prop in properties ) {
                if ( properties.hasOwnProperty(prop) && properties[prop] !== UNDEFINED && properties[prop] !== null) {
                    element[prop] = properties[prop];
                }
            }
        }
        return element;
    },
    createDomFramework : function(framework, parent) {
        var fragment = document.createDocumentFragment();
        for (var i = 0, l = framework.length; i<l; i++) {
            var UNDEFINED,
                el = framework[i],
                type = el.type,
                attributes = el.attributes || null,
                properties = el.properties || null,
                children = el.children || [],
                element = this.createElement(type, fragment, attributes, properties);
            this.createDomFramework(children, element);
        }
        if (parent) { parent.appendChild(fragment); }
        return fragment;
    }
}


window.createDomFrameworkTemplate = function(it) { return [
    {
        type:"div",
        children:[
            {
                type: "h1",
                attributes: {"class":"header"},
                properties: {innerText:it.header}
            },
            {
                type: "h2",
                attributes: {"class":"header2"},
                properties: {innerText:it.header2}
            },
            {
                type: "h3",
                attributes: {"class":"header3"},
                properties: {innerText:it.header3}
            },
            {
                type: "h4",
                attributes: {"class":"header4"},
                properties: {innerText:it.header4}
            },
            {
                type: "h5",
                attributes: {"class":"header5"},
                properties: {innerText:it.header5}
            },
            {
                type: "h6",
                attributes: {"class":"header6"},
                properties: {innerText:it.header6}
            },
            {
                type: "ul",
                attributes: {"class":"list"},
                children: function(it) {
                    var out = [];
                    for (var i = 0, l = it.list.length; i<l ; i++) {
                        out.push({
                            type: "li",
                            attributes: {"class":"item"},
                            properties: {innerText:it.list[i]}
                        });
                    }
                    return out;
                }(it)
            }
        ]
    }
]}

</script>
 
<script>
Benchmark.prototype.setup = function() {
      window.sharedVariables = {
       header: "Header",
       header2: "Header2",
       header3: "Header3",
       header4: "Header4",
       header5: "Header5",
       header6: "Header6",
       list: ['10000000', '2', '3', '4', '5', '6', '7', '8', '9', '10']
      };
};
</script>

Preparation code output

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
DOM - Mustache.js Template
$(Mustache.to_html(mustacheTemplate, sharedVariables));
++sharedVariables.list[0];
 
pending…
DOM - Twitter's Hogan.js
$(hoganTemplate.render(sharedVariables));
++sharedVariables.list[0];
 
pending…
DOM - Handlebars.js
$(handlebarsTemplate(sharedVariables));
++sharedVariables.list[0];
 
pending…
HTML - Mustache.js Template
Mustache.to_html(mustacheTemplate, sharedVariables);
++sharedVariables.list[0];
 
pending…
HTML - Twitter's Hogan.js
hoganTemplate.render(sharedVariables);
++sharedVariables.list[0];
 
pending…
HTML - DOM - Handlebars.js
Mustache.to_html(mustacheTemplate, sharedVariables);
++sharedVariables.list[0];
 
pending…
doT encodeHTML like rest
dotTemplate(sharedVariables);
++sharedVariables.list[0];
 
pending…
createDomFramework
DOM.createDomFramework(createDomFrameworkTemplate(sharedVariables)); 
++sharedVariables.list[0];
 
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment