Add benchmarks

This commit is contained in:
wycats
2010-12-28 15:47:03 -08:00
parent 0da3065987
commit 79f6b35258
3 changed files with 303 additions and 0 deletions
+21
View File
@@ -63,3 +63,24 @@ task :debug => [:compile, "dist/handlebars.debug.js"]
desc "build the build and debug versions of handlebars"
task :release => [:build, :debug]
desc "benchmark against dust.js and mustache.js"
task :bench do
require "open-uri"
File.open("vendor/mustache.js", "w") do |file|
file.puts open("https://github.com/janl/mustache.js/raw/master/mustache.js").read
file.puts "module.exports = Mustache;"
end
File.open("vendor/benchmark.js", "w") do |file|
file.puts open("https://github.com/mathiasbynens/benchmark.js/raw/master/benchmark.js").read
end
if File.directory?("vendor/dustjs")
system "cd vendor/dustjs && git pull"
else
system "git clone git://github.com/akdubya/dustjs.git vendor/dustjs"
end
system "node bench/handlebars.js"
end
+146
View File
@@ -0,0 +1,146 @@
var print = require("sys").print;
var Benchmark = require("benchmark");
var BenchWarmer = function(names) {
this.benchmarks = [];
this.currentBenches = [];
this.names = [];
this.errors = {};
};
BenchWarmer.prototype = {
winners: function(benches) {
var result = Benchmark.filter(benches, function(bench) { return bench.cycles; });
if (result.length > 1) {
result.sort(function(a, b) { return b.compare(a); });
first = result[0];
last = result[result.length - 1];
var winners = [];
Benchmark.each(result, function(bench) {
if (bench.compare(first) === 0) {
winners.push(bench);
}
});
return winners;
} else {
return result;
}
},
suite: function(suite, fn) {
this.suiteName = suite;
this.first = true;
var self = this;
fn(function(name, benchFn) {
self.push(name, benchFn);
});
},
push: function(name, fn) {
if(this.names.indexOf(name) == -1) {
this.names.push(name);
}
var first = this.first, suiteName = this.suiteName, self = this;
this.first = false;
var bench = new Benchmark(function() {
fn();
}, {
name: this.suiteName + ": " + name,
onComplete: function() {
if(first) { self.startLine(suiteName); }
self.writeBench(bench);
self.currentBenches.push(bench);
}, onError: function(bench) {
self.errors[bench.name] = bench;
}
});
this.benchmarks.push(bench);
},
bench: function() {
var benchSize = 0, names = this.names, self = this, i, l;
for(i=0, l=names.length; i<l; i++) {
var name = names[i];
if(benchSize < name.length) { benchSize = name.length; }
}
this.nameSize = benchSize + 2;
this.benchSize = 20;
var horSize = 0;
this.startLine("ops/msec");
horSize = horSize + "ops/msec ".length;
for(i=0, l=names.length; i<l; i++) {
print(names[i] + new Array(this.benchSize - names[i].length + 1).join(" "));
horSize = horSize + this.benchSize;
}
print("WINNER(S)");
horSize = horSize + "WINNER(S)".length;
print("\n" + new Array(horSize + 1).join("-"));
Benchmark.invoke(this.benchmarks, {
methodName: "run",
onComplete: function() {
var errors = false, prop, bench;
for(prop in self.errors) { if(self.errors.hasOwnProperty(prop)) { errors = true; break; } }
if(errors) {
print("\n\nErrors:\n");
for(prop in self.errors) {
if(self.errors.hasOwnProperty(prop)) {
bench = self.errors[prop];
print("\n" + bench.name + ":\n");
print(bench.error.message);
if(bench.error.stack) {
print(bench.error.stack.join("\n"));
}
print("\n");
}
}
}
}
});
},
startLine: function(name) {
var winners = Benchmark.map(this.winners(this.currentBenches), function(bench) {
return bench.name.split(": ")[1];
});
this.currentBenches = [];
print(winners.join(", "));
print("\n");
var padding = this.nameSize - name.length + 1;
name = name + new Array(padding).join(" ");
print(name);
},
writeBench: function(bench) {
var out;
if(!bench.error) {
var count = bench.hz,
min = count - bench.MoE,
max = count + bench.MoE;
out = Math.round(count / 1000) + " ±" + Math.round(bench.MoE / 1000) + " (" + bench.cycles + ")";
} else {
out = "E";
}
var padding = this.benchSize - out.length + 1;
out = out + new Array(padding).join(" ");
print(out);
}
};
module.exports = BenchWarmer;
+136
View File
@@ -0,0 +1,136 @@
require.paths.push("lib");
require.paths.push("vendor");
require.paths.push("vendor/dustjs/lib");
var BenchWarmer = require("./benchwarmer");
Handlebars = require("handlebars").Handlebars;
var dust = require("dust");
var Mustache = require("mustache");
var benchDetails = {
string: {
context: {},
handlebars: "Hello world",
dust: "Hello world",
mustache: "Hello world"
},
variables: {
context: {name: "Mick", count: 30},
handlebars: "Hello {{name}}! You have {{count}} new messages.",
dust: "Hello {name}! You have {count} new messages.",
mustache: "Hello {{name}}! You have {{count}} new messages."
},
object: {
context: { person: { name: "Larry", age: 45 } },
handlebars: "{{#with person}}{{name}}{{age}}{{/with}}",
dust: "{#person}{name}{age}{/person}",
mustache: "{{#person}}{{name}}{{age}}{{/person}}"
},
array: {
context: { names: [{name: "Moe"}, {name: "Larry"}, {name: "Curly"}, {name: "Shemp"}] },
handlebars: "{{#each names}}{{name}}{{/each}}",
dust: "{#names}{name}{/names}",
mustache: "{{#names}}{{name}}{{/names}}"
},
partial: {
context: { peeps: [{name: "Moe", count: 15}, {name: "Larry", count: 5}, {name: "Curly", count: 1}] },
partials: {
mustache: { variables: "Hello {{name}}! You have {{count}} new messages." },
handlebars: { variables: "Hello {{name}}! You have {{count}} new messages." }
},
handlebars: "{{#each peeps}}{{>variables}}{{/each}}",
dust: "{#peeps}{>variables/}{/peeps}",
mustache: "{{#peeps}}{{>variables}}{{/peeps}}"
},
recursion: {
context: { name: '1', kids: [{ name: '1.1', kids: [{name: '1.1.1', kids: []}] }] },
partials: {
mustache: { recursion: "{{name}}{{#kids}}{{>recursion}}{{/kids}}" },
handlebars: { recursion: "{{name}}{{#each kids}}{{>recursion}}{{/each}}" }
},
handlebars: "{{name}}{{#each kids}}{{>recursion}}{{/each}}",
dust: "{name}{#kids}{>recursion:./}{/kids}",
mustache: "{{name}}{{#kids}}{{>recursion}}{{/kids}}"
},
complex: {
handlebars: "<h1>{{header}}</h1>{{#if items}}<ul>{{#each items}}{{#if current}}" +
"<li><strong>{{name}}</strong></li>{{^}}" +
"<li><a href=\"{{url}}\">{{name}}</a></li>{{/if}}" +
"{{/each}}</ul>{{^}}<p>The list is empty.</p>{{/if}}",
dust: "<h1>{header}</h1>\n" +
"{?items}\n" +
" <ul>\n" +
" {#items}\n" +
" {#current}\n" +
" <li><strong>{name}</strong></li>\n" +
" {:else}\n" +
" <li><a href=\"{url}\">{name}</a></li>\n" +
" {/current}\n" +
" {/items}\n" +
" </ul>\n" +
"{:else}\n" +
" <p>The list is empty.</p>\n" +
"{/items}",
context: {
header: function() {
return "Colors";
},
items: [
{name: "red", current: true, url: "#Red"},
{name: "green", current: false, url: "#Green"},
{name: "blue", current: false, url: "#Blue"}
]
}
}
};
handlebarsTemplates = {};
var warmer = new BenchWarmer();
var makeSuite = function(name) {
warmer.suite(name, function(bench) {
var templateName = name;
var details = benchDetails[templateName];
var mustachePartials = details.partials && details.partials.mustache;
var mustacheSource = details.mustache;
var context = details.context;
bench("dust", function() {
dust.render(templateName, context, function(err, out) { });
});
bench("handlebars", function() {
handlebarsTemplates[templateName](context);
});
if(mustacheSource) {
bench("mustache", function() {
Mustache.to_html(mustacheSource, context, mustachePartials);
});
}
});
}
for(var name in benchDetails) {
if(benchDetails.hasOwnProperty(name)) {
dust.loadSource(dust.compile(benchDetails[name].dust, name));
handlebarsTemplates[name] = Handlebars.VM.compile(benchDetails[name].handlebars);
var partials = benchDetails[name].partials;
if(partials) {
for(var partialName in partials.handlebars) {
if(partials.handlebars.hasOwnProperty(partialName)) {
Handlebars.registerPartial(partialName, partials.handlebars[partialName]);
}
}
}
makeSuite(name);
}
}
warmer.bench();