jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
<h1>Sample output:</h1>
<div id="good_node">
<div>
<h1 class="Some text">Some text</h1>
<h2 class="More text">More text</h2>
<h3 title="Allows "double quotes"">Allows "double quotes"</h3>
<h4 title="& isn't &amp;">& isn't &amp;</h4>
<h5 title="<escaped ok='true'>"><escaped ok='true'></h5>
<h6 title="Bold text">Bold text</h6>
<ul class="list">
<li class="item">1 <ok></li>
<li class="item">2 <ok></li>
<li class="item">3 <ok></li>
<li class="item">4 <ok></li>
<li class="item">5 <ok></li>
<li class="item">6 <ok></li>
<li class="item">7 <ok></li>
<li class="item">8 <ok></li>
<li class="item">9 <ok></li>
<li class="item">10 <ok></li>
</ul>
</div>
</div>
<div id="sink_node" style="display:none;"></div>
<script>
var sink_node = document.getElementById('sink_node');
var good_node = document.getElementById('good_node');
function htmlencode(str) {
return (
document.createElement("i")
.appendChild(
document.createTextNode(str)
).parentNode.innerHTML
);
}
function simplify_spaces(str) {
return str.replace(/(^|>)\s+($|<)/g, '$1$2');
}
//Resig Template Function (modified to support ')
function resig_template(str) {
var strFunc =
"var p=[];" +
"with(obj){p.push('" +
str.replace(/[\r\t\n]/g, " ")
.replace(/'(?=[^#]*#>)/g, "\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(/<#=(.+?)#>/g, "',$1,'")
.split("<#").join("');")
.split("#>").join("p.push('")
+ "');}return p.join('');";
return new Function("obj", strFunc);
}
//Resig modified template function (no "with" block)
function resig_template2(str) {
var strFunc =
"var p=[];" +
"p.push('" +
str.replace(/[\r\t\n]/g, " ")
.replace(/'(?=[^#]*#>)/g, "\t")
.split("'").join("\\'")
.split("\t").join("'")
.replace(/<#=(.+?)#>/g, "',$1,'")
.split("<#").join("');")
.split("#>").join("p.push('")
+ "');return p.join('');";
return new Function("data", strFunc);
}
function jq_template(str) {
var tmpl = $.template(null, str);
return function (context) {
return tmpl($, {
data: context
}).join("");
};
}
function hogan_template(str) {
var tmpl = Hogan.compile(str);
return tmpl.render.bind(tmpl);
}
</script>
<!--External Template Definitions-->
<script type="text/x-contemplate" id="ContemplateText">
<% %>
<div>
<h1 class="<% %html($h1) %>"><% %html($h1) %></h1>
<h2 class="<% %html($h2) %>"><% %html($h2) %></h2>
<h3 title="<% %html($h3) %>"><% $h3 %></h3>
<h4 title="<% %html($h4) %>"><% %html($h4) %></h4>
<h5 title="<% %html($h5) %>"><% %html($h5) %></h5>
<h6 title="<% %html($h6) %>"><% %html($h6) %></h6>
<ul class='list'>
<% %for($list as $item) %>
<li class='item'><% %html($item) %></li>
<% %endfor() %>
</ul>
</div>
</script>
<script type="text/x-template" id="DoTTemplateText">
<div>
<h1 class='{{!it.h1}}'>{{!it.h1}}</h1>
<h2 class='{{!it.h2}}'>{{!it.h2}}</h2>
<h3 title='{{!it.h3}}'>{{!it.h3}}</h3>
<h4 title='{{!it.h4}}'>{{!it.h4}}</h4>
<h5 title='{{!it.h5}}'>{{!it.h5}}</h5>
<h6 title='{{!it.h6}}'>{{!it.h6}}</h6>
<ul class='list'>
{{~it.list :value:index}}
<li class='item'>{{!value}}</li>
{{~}}
</ul>
</div>
</script>
<script type="text/x-template" id="kendoTemplateText">
<div>
<h1 class="#: data.h1 #">#: data.h1 #</h1>
<h2 class="#: data.h2 #">#: data.h2 #</h2>
<h3 title="#: data.h3 #">#: data.h3 #</h3>
<h4 title="#: data.h4 #">#: data.h4 #</h4>
<h5 title="#: data.h5 #">#: data.h5 #</h5>
<h6 title="#: data.h6 #">#: data.h6 #</h6>
<ul class='list'>
# for (var i = 0, l = data.list.length; i < l; i++) { #
<li class='item'>#: data.list[i] #</li>
# } #
</ul>
</div>
</script>
<script type="text/x-template" id="underscoreTemplateText">
<div>
<h1 class="<%- data.h1 %>"><%- data.h1 %></h1>
<h2 class="<%- data.h2 %>"><%- data.h2 %></h2>
<h3 title="<%- data.h3 %>"><%- data.h3 %></h3>
<h4 title="<%- data.h4 %>"><%- data.h4 %></h4>
<h5 title="<%- data.h5 %>"><%- data.h5 %></h5>
<h6 title="<%- data.h6 %>"><%- data.h6 %></h6>
<ul class='list'>
<% for (var i = 0, l = data.list.length; i < l; i++) { %>
<li class='item'><%- data.list[i] %></li>
<% } %>
</ul>
</div>
</script>
<script type="text/x-template" id="resigTemplateText">
<div>
<h1 class="<#= h1 #>"><#= h1 #></h1>
<h2 class="<#= h2 #>"><#= h2 #></h2>
<h3 title="<#= h3 #>"><#= h3 #></h3>
<h4 title="<#= h4 #>"><#= h4 #></h4>
<h5 title="<#= h5 #>"><#= h5 #></h5>
<h6 title="<#= h6 #>"><#= h6 #></h6>
<ul class='list'>
<# for (var i=0, l=list.length; i < l; i++) { #>
<li class='item'><#= list[i] #></li>
<# } #>
</ul>
</div>
</script>
<script type="text/x-template" id="resig2TemplateText">
<div>
<h1 class="<#= data.h1#>"><#= data.h1 #></h1>
<h2 class="<#= data.h2#>"><#= data.h2 #></h2>
<h3 title="<#= data.h3#>"><#= data.h3 #></h3>
<h4 title="<#= data.h4#>"><#= data.h4 #></h4>
<h5 title="<#= data.h5#>"><#= data.h5 #></h5>
<h6 title="<#= data.h6#>"><#= data.h6 #></h6>
<ul class='list'>
<# for (var i=0, l=data.list.length; i < l; i++) { #>
<li class='item'><#= data.list[i] #></li>
<# } #>
</ul>
</div>
</script>
<script type="text/x-template" id="jQueryTemplateText">
<div>
<h1 class='${h1}'>${h1}</h1>
<h2 class='${h2}'>${h2}</h2>
<h3 title='${h3}'>${h3}</h3>
<h4 title='${h4}'>${h4}</h4>
<h5 title='${h5}'>${h5}</h5>
<h6 title='${h6}'>${h6}</h6>
<ul class='list'>
{{each list}}
<li class='item'>${$value}</li>
{{/each}}
</ul>
</div>
</script>
<script type="text/x-template" id="mustacheTemplateText">
<div>
<h1 class='{{h1}}'>{{h1}}</h1>
<h2 class='{{h2}}'>{{h2}}</h2>
<h3 title='{{h3}}'>{{h3}}</h3>
<h4 title='{{h4}}'>{{h4}}</h4>
<h5 title='{{h5}}'>{{h5}}</h5>
<h6 title='{{h6}}'>{{h6}}</h6>
<ul class='list'>
{{#list}}
<li class='item'>{{.}}</li>
{{/list}}
</ul>
</div>
</script>
<script type="text/x-template" id="handlebarsTemplateText">
<div>
<h1 class='{{h1}}'>{{h1}}</h1>
<h2 class='{{h2}}'>{{h2}}</h2>
<h3 title='{{h3}}'>{{h3}}</h3>
<h4 title='{{h4}}'>{{h4}}</h4>
<h5 title='{{h5}}'>{{h5}}</h5>
<h6 title='{{h6}}'>{{h6}}</h6>
<ul class='list'>
{{#each list}}
<li class='item'>{{.}}</li>
{{/each}}
</ul>
</div>
</script>
<script type="text/x-template" id="kataTemplateText">{{%(t)
<div>
<h1 class='{{t.h1}}'>{{t.h1}}</h1>
<h2 class='{{t.h2}}'>{{t.h2}}</h2>
<h3 title='{{t.h3}}'>{{t.h3}}</h3>
<h4 title='{{t.h4}}'>{{t.h4}}</h4>
<h5 title='{{t.h5}}'>{{t.h5}}</h5>
<h6 title='{{t.h6}}'>{{t.h6}}</h6>
<ul class='list'>
{{@(t.list)(l)
<li class='item'>{{l}}</li>
@}}
</ul>
</div>
%}}</script>
<script type="text/html" id="template-kingkong">
<div>
<h1 class="<#- data.h1 #>"><#- data.h1#></h1>
<h2 class="<#- data.h2#>"><#- data.h2 #></h2>
<h3 title="<#- data.h3#>"><#- data.h3 #></h3>
<h4 title="<#- data.h4#>"><#- data.h4 #></h4>
<h5 title="<#- data.h5#>"><#- data.h5 #></h5>
<h6 title="<#- data.h6#>"><#- data.h6 #></h6>
<ul class='list'>
<# for (var i=0, l=data.list.length; i < l; i++) { #>
<li class='item'><#- data.list[i] #></li>
<# } #>
</ul>
</div>
</script>
<!-- contemplate engine -->
<script src="http://foo123.github.io/libs/Contemplate-0.6.7.min.js"></script>
<script src="https://code.jquery.com/jquery-2.0.3.min.js"></script>
<script src="https://olado.github.io/doT/doT.min.js"></script>
<script src="https://documentcloud.github.com/underscore/underscore.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/0.7.2/mustache.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/hogan.js/2.0.0/hogan.js"></script>
<script src="http://builds.handlebarsjs.com.s3.amazonaws.com/handlebars-v1.1.2.js"></script>
<script src="https://cdn.kendostatic.com/2013.3.1119/js/kendo.all.min.js"></script>
<script src="https://jashkenas.github.com/coffee-script/extras/coffee-script.js"></script>
<script src="https://ajax.microsoft.com/ajax/jquery.templates/beta1/jquery.tmpl.min.js"></script>
<script src="http://rawgithub.com/smcmurray/kata/master/kata.min.js"></script>
<script>
var aro = {};
aro.string = new function ()
{
/**
* @var {Object}
*/
this.escapes_html =
{
'\\': '\',
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": ''',
"/": '/'
};
/**
* @var {RexExp}
*/
this.escape_html_reg = /[&<>"'\/\\]/g;
/**
* @param {String} string
* @returns {String}
*/
this.escapeHtml = function(string)
{
return string.replace(
this.escape_html_reg,
function (match)
{
return aro.string.escapes_html[match];
}
);
};
};
aro.template = new function()
{
/**
* @var {String}
*/
this.opener = '<#',
/**
* @var {String}
*/
this.closer = '#>';
/**
* Retourne le corp d'une fonction correspondante à un template
*
* @param {String} template
* @param {Object} options
* @returns {String}
*/
function transform(template, options)
{
var fn_body = 'var r=\'\';',
code = false,
code_op = 0,//1 == concat, 2 == concat + escape
is_last_code_block = true,
is_escape = false,
char = '',
from = 0,
opener_chars = (options.opener || aro.template.opener).split(''),
closer_chars = (options.closer || aro.template.closer).split(''),
opener_chars_len = opener_chars.length,
closer_chars_len = closer_chars.length,
frag,
j, to, l, char;
source : for(to = 0, l = template.length - (closer_chars_len - 1); to < l; to++)
{
char = template[to];
//ouverture block code
if(!code)
{
if(!is_escape && char === opener_chars[0])
{
for(j = 1; j < opener_chars_len; j++)
{
if(opener_chars[j] !== template[to + j])
{
continue source;
}
}
code = true;
if(from < to)
{
fn_body += (is_last_code_block ? 'r+=' : '+') + JSON.stringify(template.slice(from, to));
}
to += opener_chars_len - 1;
from = to;
}
}
//code concat
else if(!code_op && from === to - 1)
{//alert(char);
if(char === '=')
{
from++;
code_op = 1;
is_last_code_block = false;
}
else if(char === '-')
{
from++;
code_op = 2;
is_last_code_block = false;
}
else
{
code_op = 0;
is_last_code_block = true;
from && (fn_body += ';');
}
}
//fermeture block code
else if(!is_escape && char === closer_chars[0])
{
for(j = 1; j < closer_chars_len; j++)
{
if(closer_chars[j] !== template[to + j])
{
continue source;
}
}
if(from < to)
{
frag = template.slice(from + opener_chars_len, to);//alert(frag);
fn_body += !code_op ? frag : (is_last_code_block ? 'r+=' : '+') +
(code_op === 2 ? 'aro.string.escapeHtml(' + frag + ')' : frag);
}
code = false;
code_op = 0;
from = to + closer_chars_len;
to = from;
}
is_escape = char === '\\';
}
if(from < to - 1)
{
fn_body += (is_last_code_block ? 'r+=' : '+') + JSON.stringify(template.slice(from, to)) + ';';
}
return fn_body += 'return r;';
}
/**
* Retourne la fonction correspondante au template sous forme de chaine
*
* @param {String} template
* @param {Object} options
* @param {String} name
* @returns {String}
*/
this.getSource = function(template, options, name)
{
return 'function ' + (name || '') + '(data){' + transform(template, options || {}) + '}';
};
/**
* Retourne la fonction correspondante au template
*
* @param {String} template
* @param {Object} options
* @returns {Function}
*/
this.compile = function(template, options)
{
return Function(
'data',
transform(template, options || {})
);
};
};
</script>
<script>
window.sharedVariables = {
h1: "Some text",
h2: "More text",
h3: "Allows \"double quotes\"",
h4: "& isn't &",
h5: "<escaped ok='true'>",
h6: "Bold text",
list: ['1 <ok>', '2 <ok>', '3 <ok>', '4 <ok>', '5 <ok>', '6 <ok>', '7 <ok>', '8 <ok>', '9 <ok>', '10 <ok>']
};
Contemplate.add({
'test': '#ContemplateText'
});
ContemplateTpl = Contemplate.tpl('test');
function getTemplate(id) { return document.getElementById(id).textContent; }
var templates = {
Contemplate: function(data){ return ContemplateTpl.render(data); },
DoT: doT.compile(getTemplate("DoTTemplateText"), { append: false }),
DoT_append: doT.compile(getTemplate("DoTTemplateText"), { append: true }),
jQuery: jq_template(getTemplate("jQueryTemplateText")),
Mustache: Mustache.compile(getTemplate("mustacheTemplateText")),
Hogan: hogan_template(getTemplate("mustacheTemplateText")),
Handlebars: Handlebars.compile(getTemplate("handlebarsTemplateText")),
KendoUI: kendo.template(getTemplate("kendoTemplateText")),
Underscore: _.template(getTemplate("underscoreTemplateText"), null, { variable: 'data' }),
Resig: resig_template(getTemplate("resigTemplateText")),
Resig_no_with: resig_template2(getTemplate("resig2TemplateText")),
Kata: kata(getTemplate("kataTemplateText")),
aro: aro.template.compile(getTemplate("template-kingkong"))
}
function mkTest(template, name) {
return function () {
sink_node.setAttribute('data-generator', name);
sink_node.innerHTML = template(sharedVariables);
};
}
var test = {};
for (var name in templates) {
test[name] = mkTest(templates[name], name);
}
</script>
while (sink_node.firstChild)
sink_node.removeChild(sink_node.firstChild);
var sink_html = simplify_spaces(sink_node.innerHTML);
var good_html = simplify_spaces(good_node.innerHTML);
if (sink_html !== good_html) {
throw {
generator: htmlencode(sink_node.getAttribute('data-generator')),
wrong_output: htmlencode(sink_html),
right_output: htmlencode(good_html)
};
}
Ready to run.
Test | Ops/sec | |
---|---|---|
Handlebars.js |
| ready |
Underscore.js Template |
| ready |
Mustache Compiled Template |
| ready |
Hogan CompiledTemplate |
| ready |
Kata Template |
| ready |
doT Template |
| ready |
Contemplate |
| ready |
aro |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.