Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Adding mustache.js benchmark

  • Loading branch information...
commit a40d37ebb6724e83b47d0c56bcea7c134ea2ba36 1 parent 292d03a
Chris Williams authored
Showing with 268 additions and 0 deletions.
  1. +27 −0 benchmarks/benchmark-mustache.js
  2. +241 −0 benchmarks/mustache.js
View
27 benchmarks/benchmark-mustache.js
@@ -0,0 +1,27 @@
+// to run: node benchmark-mustache.js
+var sys = require("sys"),
+ mustache = require('./mustache');
+
+n = 5000
+
+sys.puts('Running ' + n + ' times')
+
+function benchmark(label, fn) {
+ var start = Number(new Date)
+ for (var i = 0; i < n; ++i) fn()
+ sys.puts(label + ': ' + (Number(new Date) - start).toString() + 'ms')
+}
+
+var template='<html><head><title>{{title}}</title></head><body><h1>{{title}}</h1>{{#articles}}<div class="article"><h2>{{title}}</h2><p>{{body}}</p>{{#published}}<p>{{title}} is published</p></div>{{/articles}}</body></html>';
+
+benchmark('large template', function(){
+ mustache.to_html(template, {
+ title: 'Articles',
+ articles: [
+ { title: 'One', body: 'Some foo bar' },
+ { title: 'Two', body: 'Some foo bar' },
+ { title: 'Three', body: 'Some foo bar', published: true },
+ { title: 'Four', body: 'Some foo bar', published: true }
+ ]
+ });
+});
View
241 benchmarks/mustache.js
@@ -0,0 +1,241 @@
+/*
+ Shamless port of http://github.com/defunkt/mustache
+ by Jan Lehnardt <jan@apache.org>, Alexander Lang <alex@upstream-berlin.com>,
+ Sebastian Cohnen <sebastian.cohnen@googlemail.com>
+
+ Thanks @defunkt for the awesome code.
+
+ See http://github.com/defunkt/mustache for more info.
+*/
+
+var Mustache = function() {
+ var Renderer = function() {};
+
+ Renderer.prototype = {
+ otag: "{{",
+ ctag: "}}",
+
+ render: function(template, context, partials) {
+ // fail fast
+ if(template.indexOf(this.otag) == -1) {
+ return template;
+ }
+
+ var html = this.render_section(template, context, partials);
+ return this.render_tags(html, context, partials);
+ },
+
+ /*
+ Tries to find a partial in the global scope and render it
+ */
+ render_partial: function(name, context, partials) {
+ if(typeof(context[name]) != "object") {
+ throw({message: "subcontext for '" + name + "' is not an object"});
+ }
+ if(!partials || !partials[name]) {
+ throw({message: "unknown_partial"});
+ }
+ return this.render(partials[name], context[name], partials);
+ },
+
+ /*
+ Renders boolean and enumerable sections
+ */
+ render_section: function(template, context, partials) {
+ if(template.indexOf(this.otag + "#") == -1) {
+ return template;
+ }
+ var that = this;
+ // CSW - Added "+?" so it finds the tighest bound, not the widest
+ var regex = new RegExp(this.otag + "\\#(.+)" + this.ctag +
+ "\\s*([\\s\\S]+?)" + this.otag + "\\/\\1" + this.ctag + "\\s*", "mg");
+
+ // for each {{#foo}}{{/foo}} section do...
+ return template.replace(regex, function(match, name, content) {
+ var value = that.find(name, context);
+ if(that.is_array(value)) { // Enumerable, Let's loop!
+ return that.map(value, function(row) {
+ return that.render(content, that.merge(context,
+ that.create_context(row)), partials);
+ }).join('');
+ } else if(value) { // boolean section
+ return that.render(content, context, partials);
+ } else {
+ return "";
+ }
+ });
+ },
+
+ /*
+ Replace {{foo}} and friends with values from our view
+ */
+ render_tags: function(template, context, partials) {
+ var lines = template.split("\n");
+
+ var new_regex = function() {
+ return new RegExp(that.otag + "(=|!|<|\\{)?([^\/#]+?)\\1?" +
+ that.ctag + "+", "g");
+ };
+
+ // tit for tat
+ var that = this;
+
+ var regex = new_regex();
+ for (var i=0; i < lines.length; i++) {
+ lines[i] = lines[i].replace(regex, function (match,operator,name) {
+ switch(operator) {
+ case "!": // ignore comments
+ return match;
+ case "=": // set new delimiters, rebuild the replace regexp
+ that.set_delimiters(name);
+ regex = new_regex();
+ // redo the line in order to get tags with the new delimiters
+ // on the same line
+ i--;
+ return "";
+ case "<": // render partial
+ return that.render_partial(name, context, partials);
+ case "{": // the triple mustache is unescaped
+ return that.find(name, context);
+ default: // escape the value
+ return that.escape(that.find(name, context));
+ }
+ },this);
+ };
+ return lines.join("\n");
+ },
+
+ set_delimiters: function(delimiters) {
+ var dels = delimiters.split(" ");
+ this.otag = this.escape_regex(dels[0]);
+ this.ctag = this.escape_regex(dels[1]);
+ },
+
+ escape_regex: function(text) {
+ // thank you Simon Willison
+ if(!arguments.callee.sRE) {
+ var specials = [
+ '/', '.', '*', '+', '?', '|',
+ '(', ')', '[', ']', '{', '}', '\\'
+ ];
+ arguments.callee.sRE = new RegExp(
+ '(\\' + specials.join('|\\') + ')', 'g'
+ );
+ }
+ return text.replace(arguments.callee.sRE, '\\$1');
+ },
+
+ /*
+ find `name` in current `context`. That is find me a value
+ from the view object
+ */
+ find: function(name, context) {
+ name = this.trim(name);
+ if(typeof context[name] === "function") {
+ return context[name].apply(context);
+ }
+ if(context[name] !== undefined) {
+ return context[name];
+ }
+ throw({message: "'" + name + "' not found in context"});
+ },
+
+ // Utility methods
+
+ /*
+ Does away with nasty characters
+ */
+ escape: function(s) {
+ return s.toString().replace(/[&"<>\\]/g, function(s) {
+ switch(s) {
+ case "&": return "&amp;";
+ case "\\": return "\\\\";;
+ case '"': return '\"';;
+ case "<": return "&lt;";
+ case ">": return "&gt;";
+ default: return s;
+ }
+ });
+ },
+
+ /*
+ Merges all properties of object `b` into object `a`.
+ `b.property` overwrites a.property`
+ */
+ merge: function(a, b) {
+ var _new = {};
+ for(var name in a) {
+ if(a.hasOwnProperty(name)) {
+ _new[name] = a[name];
+ }
+ };
+ for(var name in b) {
+ if(b.hasOwnProperty(name)) {
+ _new[name] = b[name];
+ }
+ };
+ return _new;
+ },
+
+ create_context: function(_context) {
+ if(this.is_object(_context)) {
+ return _context;
+ } else {
+ return {'.': _context};
+ }
+ },
+
+ is_object: function(a) {
+ return a && typeof a == 'object'
+ },
+
+ /*
+ Thanks Doug Crockford
+ JavaScript — The Good Parts lists an alternative that works better with
+ frames. Frames can suck it, we use the simple version.
+ */
+ is_array: function(a) {
+ return (a &&
+ typeof a === 'object' &&
+ a.constructor === Array);
+ },
+
+ /*
+ Gets rid of leading and trailing whitespace
+ */
+ trim: function(s) {
+ return s.replace(/^\s*|\s*$/g, '');
+ },
+
+ /*
+ Why, why, why? Because IE. Cry, cry cry.
+ */
+ map: function(array, fn) {
+ if (typeof array.map == "function") {
+ return array.map(fn)
+ } else {
+ var r = [];
+ var l = array.length;
+ for(i=0;i<l;i++) {
+ r.push(fn(array[i]));
+ }
+ return r;
+ }
+ }
+ };
+
+ return({
+ name: "mustache.js",
+ version: "0.1",
+
+ /*
+ Turns a template and view into HTML
+ */
+ to_html: function(template, view, partials) {
+ return new Renderer().render(template, view, partials);
+ }
+ });
+}();
+
+
+exports.to_html = Mustache.to_html;

6 comments on commit a40d37e

@tj

Do we have to add all this stuff? plus comparing V8 to Rhino isnt overly fair, evented IO will speed up the mustache.js benchmarks im sure but we should benchmark mojo with node too

@tj

The benchmark I did was a quick test with Rhino

@voodootikigod

Most people programming modern commonJS implementations are going to be using V8 or TraceMonkey, its where the speed is. Rhino, though being complete is woefully underperformant in comparison. If the benchmark was meant to show that mojo (C) is faster than JavaScript Interpretation through Java, I think thats a bit obvious. a C compiler in contrast to a C/C++ JavaScript interpreter - the value decreases. Thats all I was showing, apples to apples. FWIW in TraceMonkey it yields roughly the same time.

@voodootikigod

Also, Evented IO really doesn't affect regular expression processing (which is what Mustache.js uses).

@voodootikigod

also, raw v8 time is actually lower than node.js averaging around 877ms

@janl

I like the benchmarks because it allows us to tweak mustache.js's performance. I don't see much need for comparing engines tho :)

Please sign in to comment.
Something went wrong with that request. Please try again.