Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
381 lines (345 sloc) 18.1 KB
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
<title>ECMAScript Templating Benchmarks // aefxx.com</title>
<link type="image/x-icon" href="favicon.ico" rel="Shortcut Icon">
<link rel="stylesheet" href="external/styles.css" type="text/css"/>
<script src="external/jquery-1.4.2.min.js" type="text/javascript"></script>
<script src="external/jquery.benchmark.js" type="text/javascript"></script>
<script src="external/jquery.flot.min.js" type="text/javascript"></script>
<script src="external/jquery.mustache.js" type="text/javascript"></script>
<script src="external/jquery.tempest.js" type="text/javascript"></script>
<script src="external/jquery.tmpl.js" type="text/javascript"></script>
<script src="external/underscore.js" type="text/javascript"></script>
<script src="external/jquery.srender.js" type="text/javascript"></script>
<script src="external/jquery.nano.js" type="text/javascript"></script>
<script src="external/ejs_production.js" type="text/javascript"></script>
<script src="external/pure.js" type="text/javascript"></script>
<script src="jquery.jqote2.js" type="text/javascript"></script>
</head>
<body>
<h1>ECMAScript Templating Benchmarks</h1>
<hr/>
<h2>&copy;2010 aefxx // powered by jQuery // idea taken from Brian Landau</h2>
<ul id="contestants">
<li>
<p><input type="checkbox" name="contestant" value="srender"/> srender</p>
<ul class="progress srender"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="mustache_js"/> mustache.js</p>
<ul class="progress mustache_js"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="underscore"/> Underscore</p>
<ul class="progress underscore"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="jqote2"/> jQote2</p>
<ul class="progress jqote2"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="tempest"/> Tempest</p>
<ul class="progress tempest"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="nano"/> nano</p>
<ul class="progress nano"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="tmpl"/> jQuery Templating Plugin</p>
<ul class="progress tmpl"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="ejs"/> embeddedjavascript</p>
<ul class="progress ejs"></ul>
<p class="number"></p>
</li>
<li>
<p><input type="checkbox" name="contestant" value="pure"/> pure</p>
<ul class="progress pure"></ul>
<p class="number"></p>
</li>
<li>
<input type="checkbox" name="check" value="0"/> Check all
</li>
<li>
<button id="run">RUN</button>
&#xa0;&#xa0;Cycles:&#xa0;
5x <input type="radio" name="cycles" value="5" checked="checked"/>
10x <input type="radio" name="cycles" value="10"/>
25x <input type="radio" name="cycles" value="25"/>
50x <input type="radio" name="cycles" value="50"/>
&#xa0;&#xa0;Types:&#xa0;
Simple <input type="checkbox" name="simple_test" value="1" checked="checked"/>
Loop <input type="checkbox" name="loop_test" value="2" checked="checked"/>
</li>
</ul>
<h2>█ Single Passed Run &#xa0;&#xa0; Median in ms [Arithm. AVG in ms]</h2>
<div id="placeholder"></div>
<div id="pure">
<div class="test">
<h2></h2>
<p>The homepage is <a></a>.</p>
<p></p>
</div>
<div class="comments">
<h3></h3>
<ul>
<li class="comment">
<h5><%= this.comments[n].name %></h5>
<p><%= this.comments[n].body %></p>
</li>
</ul>
</div>
</div>
<script type="text/x-jqote-template" id="jqote2_simple">
<![CDATA[<div class="test"><h2>This is a test of <%= this.name %></h2><p>The homepage is <a href="<%= this.url %>"><%= this.url %></a>.</p><p>The sources is: <%= this.source %></p></div>]]>
</script>
<script type="text/x-jqote-template" id="jqote2_loop">
<![CDATA[<div class="comments"><h3><%= this.header %></h3><ul><% for ( var n=0,m=this.comments.length;n<m;n++ ) { %><li class="comment"><h5><%= this.comments[n].name %></h5><p><%= this.comments[n].body %></p></li><% } %></ul></div>]]>
</script>
<script type="text/x-ejs" id="ejs_simple">
<div class="test"><h2>This is a test of [%= name %]</h2><p>The homepage is <a href="[%= url %]">[%= url %]</a>.</p><p>The sources is: [%= source %]</p></div>
</script>
<script type="text/x-ejs" id="ejs_loop">
<div class="comments"><h3>[%= header %]</h3><ul>[% $.each(comments, function(i, comment){ %]<li class="comment"><h5>[%= comment.name %]</h5><p>[%= comment.body %]</p></li>[% }); %]</ul></div>
</script>
<script type="text/x-jquery-tmpl" id="tmpl_simple">
<![CDATA[<div class="test"><h2>This is a test of ${name}</h2><p>The homepage is <a href="${url}">${this.url}</a>.</p><p>The sources is: ${source}</p></div>]]>
</script>
<script type="text/x-jquery-tmpl" id="tmpl_loop">
<![CDATA[<div class="comments"><h3>${header}</h3><ul>{{each(i, comment) comments}}<li class="comment"><h5>${comment.name}</h5><p>${comment.body}</p></li>{{/each}}</ul></div>]]>
</script>
<script type="text/javascript">
var CYCLES = $('input:radio:checked').val(),
CONVERSIONS = 1000,
RUN_LEAP = Math.round(CONVERSIONS * 0.5),
contestants = {};
function shuffle(v) {
for ( var j, x, i = v.length; i; j = parseInt(Math.random() * i), x = v[--i], v[i] = v[j], v[j] = x );
return v;
};
function mean(array) {
if ( !array.length ) return 0;
var sum = 0;
for ( var i=0; i < array.length; i++ )
sum += parseFloat(array[i], 4);
return (1/array.length) * sum;
}
function median(array) {
if ( !array.length ) return 0;
var s = array.sort(function(a, b) {return a - b;}).length;
return s % 2 ?
array[(s-1)/2] : (array[(s/2)-1] + array[s/2]) / 2;
}
function plot(cons) {
var data = [], i = 1;
for ( key in cons ) {
var result = {
median: median(cons[key].results).toPrecision(2)*1000,
mean: mean(cons[key].results).toPrecision(2)*1000
};
data.push({
label: cons[key].name,
data: [[i++, result.median], [i++,null]],
color: cons[key].color,
bars: {
show: true,
barWidth: 1,
lineWidth: 1,
fill: 1,
colors: cons[key].color
}
});
if ( cons[key].results.length )
cons[key].number.text(result.median+' ms ['+result.mean+' ms]');
}
$.plot($('#placeholder'), data, {
xaxis: { ticks: [[1.5, 'Srender'], [3.5, 'mustache.js'], [5.5, 'Underscore'], [7.5, 'jQote2'], [9.5, 'Tempest'], [11.5, 'nano'], [13.5, 'jQuery Templating Plugin'], [15.5, 'embeddedjavascript'], [17.5, 'pure']], autoscaleMargin: .02 },
yaxis: { min: 10, max: 150 },
legend: { position: 'ne' },
grid: { backgroundColor: '#ffffff' }
});
}
$(function() {
var benchmarks = {
srender: {
simple: function() {$.srender(this.simple, payload.simple);},
loop: function() {$.srender(this.loop, payload.loop);}
},
mustache_js: {
simple: function() {$.mustache(this.simple, payload.simple);},
loop: function() {$.mustache(this.loop, payload.loop);}
},
underscore: {
simple: function() {this.simple(payload.simple);},
loop: function() {this.loop(payload.loop);}
},
jqote2: {
simple: function() {$.jqote(this.simple, payload.simple);},
loop: function() {$.jqote(this.loop, payload.loop);}
},
tempest: {
simple: function() {$.tempest(this.simple, payload.simple);},
loop: function() {$.tempest(this.loop, payload.loop);}
},
nano: {
simple: function() {$.nano(this.simple, payload.simple);},
loop: function() {
var nano = {comments: '', header: payload.loop.header};
for ( var i=0; i < payload.loop.comments.length; i++ )
nano.comments += $.nano(this.loop.comment, payload.loop.comments[i]);
$.nano(this.loop.container, nano);
}
},
tmpl: {
simple: function() {this.simple($, {data: payload.simple}).join("");},
loop: function() {this.loop($, {data: payload.loop}).join("");}
},
ejs: {
simple: function() {this.simple.render(payload.simple);},
loop: function() {this.loop.render(payload.loop);}
},
pure: {
simple: function() {this.simple(payload.simple);},
loop: function() {this.loop(payload.loop);}
}
};
var templates = {
mustache_js: {
simple: '<div class="test"><h2>This is a test of {{name}}</h2><p>The homepage is <a href="{{url}}">{{url}}</a>.</p><p>The sources is: {{source}}</p></div>',
loop: '<div class="comments"><h3>{{header}}</h3><ul>{{#comments}}<li class="comment"><h5>{{name}}</h5><p>{{body}}</p></li>{{/comments}}</ul></div>'
},
underscore: {
simple: _.template('<div class="test"><h2>This is a test of <%= name %></h2><p>The homepage is <a href="<%= url %>"><%= url %></a>.</p><p>The sources is: <%= source %></p></div>'),
loop: _.template('<div class="comments"><h3><%= header %></h3><ul><% _.each(comments, function(comment){ %><li class="comment"><h5><%= comment.name %></h5><p><%= comment.body %></p></li><% }); %></ul></div>')
},
srender: {
simple: '<div class="test"><h2>This is a test of <%= name %></h2><p>The homepage is <a href="<%= url %>"><%= url %></a>.</p><p>The sources is: <%= source %></p></div>',
loop: '<div class="comments"><h3><%= header %></h3><ul><% $.each(comments, function(i, comment){ %><li class="comment"><h5><%= comment.name %></h5><p><%= comment.body %></p></li><% }); %></ul></div>'
},
jqote2: {
simple: $.jqotec('#jqote2_simple'),
loop: $.jqotec('#jqote2_loop')
},
tempest: {
simple: $.tempest('simple', '<div class="test"><h2>This is a test of {{name}}</h2><p>The homepage is <a href="{{url}}">{{url}}</a>.</p><p>The sources is: {{source}}</p></div>') && 'simple',
loop: $.tempest('loop', '<div class="comments"><h3>{{header}}</h3><ul>{% for comment in comments %}<li class="comment"<h5>{{comment.name}}</h5><p>{{comment.body}}</p></li>{% endfor %}</ul></div>') && 'loop'
},
nano: {
simple: '<div class="test"><h2>This is a test of {name}</h2><p>The homepage is <a href="{url}">{url}</a>.</p><p>The sources is: {source}</p></div>',
loop: {
comment: '<li class="comment"><h5>{name}</h5><p>{body}</p></li>',
container: '<div class="comments"><h3>{header}</h3><ul>{comments}</ul></div>'
}
},
tmpl: {
simple: $('#tmpl_simple').template(),
loop: $('#tmpl_loop').template()
},
ejs: {
simple: new EJS({element: $('#ejs_simple')[0]}),
loop: new EJS({element: $('#ejs_loop')[0]})
},
pure: {
simple: $('#pure div.test').compile({
'h2': 'This is a test of #{name}',
'p a': 'url',
'p a@href': 'url',
'p:last-child': 'The sources is: #{source}'
}),
loop: $('#pure div.comments').compile({
'h3': 'header',
'li': {
'comment<-comments': {
'h5': 'comment.name',
'p': 'comment.body'
}
}
})
}
};
var payload = {
simple: {
name: 'foo',
url: 'http://foo.bar/foo',
source: 'http://foo.bar/jquery.foo.js'
},
loop: {
header: "My Post Comments",
comments: [
{name: "Joe", body: "Thanks for this post!"},
{name: "Sam", body: "Thanks for this post!"},
{name: "Heather", body: "Thanks for this post!"},
{name: "Kathy", body: "Thanks for this post!"},
{name: "George", body: "Thanks for this post!"}
]
}
};
var color = {
mustache_js: '#8f04a8',
underscore: '#cd0074',
srender: '#5b4cd8',
jqote2: '#70e500',
tempest: '#0d3349',
nano: '#fff800',
tmpl: '#00f8ff',
ejs: '#f800ff',
pure: '#ff9900'
};
$('input[name=check]').click(function() {
var checked = this.checked;
$('input[name=contestant]').each(function() {
this.checked = checked;
});
});
$('input[name=contestant]').each(function(i) {
var key = this.value;
contestants[key] = {
name: key,
results: [],
input: $(this),
color: color[key],
number: $('p.number', $(this).parents('li')[0]),
progress: $('ul.progress', $(this).parents('li')[0]),
templates: templates[key],
benchmarks: benchmarks[key]
};
}).click(function() {
$('input[name=check]')[0].checked = false;
});
$('#run').click(function() {
CYCLES = $('input:radio:checked').val();
$('ul.progress, p.number').empty();
$(this).trigger('benchmark');
}).bind('benchmark', function() {
var cons = shuffle($('input[name=contestant]:checked').toArray()),
runs = cons.length;
if ( !runs ) return;
var test_run = setInterval(function() {
var contestant = null;
if ( !(contestant = cons.shift()) ) return;
if ( $('input[name=simple_test]:checked').length )
$.benchmark(CONVERSIONS, contestants[contestant.value], 'simple');
if ( $('input[name=loop_test]:checked').length )
$.benchmark(CONVERSIONS, contestants[contestant.value], 'loop');
contestants[contestant.value].progress.append('<li/>');
}, RUN_LEAP);
setTimeout(function() {
clearInterval(test_run);
( --CYCLES ) ? $('#run').trigger('benchmark') : plot(contestants);
}, RUN_LEAP * runs + 1500);
});
});
</script>
</body>
</html>