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
Testing the performance of various router implementations
<script src="https://cdnjs.cloudflare.com/ajax/libs/Director/1.2.8/director.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/backbone.js/1.2.0/backbone-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/page.js/1.6.3/page.min.js"></script>
<!--Required for crossroads-->
<script src="https://cdnjs.cloudflare.com/ajax/libs/js-signals/1.0.0/js-signals.min.js"></script>
<script
src="https://cdnjs.cloudflare.com/ajax/libs/crossroads/0.12.1/crossroads.min.js"></script>
<script src="https://cdn.rawgit.com/dstillman/pathparser.js/master/pathparser.min.js"></script>
<script>
function Prev() {
var routes = {},
decode = decodeURIComponent;
function noop(s) { return s; }
function sanitize(url) {
url.indexOf('/?') >= 0 && (url = url.replace('/?', '?'));
url[0] == '/' && (url = url.slice(1));
url[url.length - 1] == '/' && (url = url.slice(0, -1));
return url;
}
function processUrl(url) {
var esc = url.indexOf('%') >= 0 ? decode : noop,
pieces = url.split('/'),
rules = routes,
params = {};
for (var i = 0; i < pieces.length && rules; ++i) {
var piece = esc(pieces[i]);
rules = rules[piece.toLowerCase()] || rules[':'];
rules && rules[':'] && (params[rules[':']] = piece);
}
return rules && {
cb: rules['@'],
params: params
};
}
function processQuery(url, params) {
if (url) {
var hash = url.indexOf('#'),
esc = url.indexOf('%') >= 0 ? decode : noop,
query = hash >= 0
? url.slice(0, hash).split('&')
: url.split('&');
for (var i = 0; i < query.length; ++i) {
var nameValue = query[i].split('=');
params[nameValue[0]] = esc(nameValue[1]);
}
}
return params;
}
return {
add: function(route, handler) {
var pieces = route.toLowerCase().split('/'),
rules = routes;
for (var i = 0; i < pieces.length; ++i) {
var piece = pieces[i],
name = piece[0] == ':' ? ':' : piece;
rules = rules[name] || (rules[name] = {});
name == ':' && (rules[':'] = piece.slice(1));
}
rules['@'] = handler;
},
run: function(url) {
url = sanitize(url);
var querySplit = url.split('?'),
result = processUrl(querySplit[0]) || {};
result.cb && processQuery(querySplit[1], result.params) && result.cb({
url: url,
params: result.params
});
return !!result.cb;
}
};
}
function Rlite() {
var routes = {},
decode = decodeURIComponent;
function noop(s) { return s; }
function sanitize(url) {
url.indexOf('/?') >= 0 && (url = url.replace('/?', '?'));
url[0] == '/' && (url = url.slice(1));
url[url.length - 1] == '/' && (url = url.slice(0, -1));
return url;
}
function processUrl(url) {
var esc = url.indexOf('%') >= 0 ? decode : noop,
pieces = url.split('/'),
rules = routes,
params = {};
for (var i = 0; i < pieces.length && rules; ++i) {
var piece = esc(pieces[i]);
rules = rules[piece.toLowerCase()] || rules[':'];
rules && rules[':'] && (params[rules[':']] = piece);
}
return rules && {
cb: rules['@'],
params: params
};
}
function processQuery(url, ctx) {
if (url) {
var hash = url.indexOf('#'),
esc = url.indexOf('%') >= 0 ? decode : noop,
query = (hash < 0 ? url : url.slice(0, hash)).split('&');
for (var i = 0; i < query.length; ++i) {
var nameValue = query[i].split('=');
ctx.params[nameValue[0]] = esc(nameValue[1]);
}
}
return ctx;
}
function lookup(url) {
var querySplit = sanitize(url).split('?');
return processQuery(querySplit[1], processUrl(querySplit[0]) || {});
}
return {
add: function(route, handler) {
var pieces = route.toLowerCase().split('/'),
rules = routes;
for (var i = 0; i < pieces.length; ++i) {
var piece = pieces[i],
name = piece[0] == ':' ? ':' : piece;
rules = rules[name] || (rules[name] = {});
name == ':' && (rules[':'] = piece.slice(1));
}
rules['@'] = handler;
},
exists: function (url) {
return !!lookup(url).cb;
},
run: function(url) {
var result = lookup(url);
result.cb && result.cb({
url: url,
params: result.params
});
return !!result.cb;
}
};
}
/// Adding routes is not perf-critical, but running them is
var rliteNew = Rlite();
var rliteOld = Prev();
rliteNew.add('hello/:id', function (ctx) {
return ctx.params.id.length;
});
rliteNew.add('hello/world', function (ctx) {
return ctx;
});
rliteNew.add('hello/jones', function (ctx) {
return ctx;
});
rliteNew.add('hello/worlds', function (ctx) {
return ctx;
});
rliteNew.add('by/:h3', function (ctx) {
return ctx.params.h3.length;
});
rliteNew.add('by/h43', function (ctx) {
return ctx;
});
rliteOld.add('hello/:id', function (ctx) {
return ctx.params.id.length;
});
rliteOld.add('hello/world', function (ctx) {
return ctx;
});
// Director
var dir = Router().init();
dir.on('/hello/:id', function (ctx) {
return ctx.length;
});
dir.on('/hello/world', function (ctx) {
return ctx;
});
dir.on('/hello/jones', function (ctx) {
return ctx;
});
dir.on('/hello/worlds', function (ctx) {
return ctx;
});
dir.on('/by/:h3', function (ctx) {
return ctx.length;
});
dir.on('/by/h43', function (ctx) {
return ctx;
});
// Backbone
var bb = new Backbone.Router();
Backbone.history.start();
bb.route('hello/:id', 'hello');
bb.on('hello', function (ctx) {
return ctx.length;
});
bb.route('hello/world', 'world');
bb.on('world', function (ctx) {
return ctx;
});
bb.route('hello/jones', 'jones');
bb.on('jonew', function (ctx) {
return ctx;
});
bb.route('hello/worlds', 'worlds');
bb.on('worlds', function (ctx) {
return ctx;
});
bb.route('by/:h3', 'h3');
bb.on('h3', function (ctx) {
return ctx;
});
bb.route('by/h43', 'h43');
bb.on('h43', function (ctx) {
return ctx;
});
//Page
page('/hello/:id', function (ctx) {
return ctx.params.id;
});
page('/hello/world', function (ctx) {
return ctx;
});
page('/hello/jones', function (ctx) {
return ctx;
});
page('/hello/worlds', function (ctx) {
return ctx;
});
page('/by/:h3', function (ctx) {
return ctx.params.h3;
});
page('/by/h43', function (ctx) {
return ctx;
});
page('*', function () {});
//Crossroads
crossroads.addRoute('/hello/{id}', function (id) {
return id;
});
crossroads.addRoute('/hello/world', function (ctx) {
return ctx;
});
crossroads.addRoute('/hello/jones', function (ctx) {
return ctx;
});
crossroads.addRoute('/hello/worlds', function (ctx) {
return ctx;
});
crossroads.addRoute('/by/{h3}', function (h3) {
return h3;
});
crossroads.addRoute('/by/h43', function (ctx) {
return ctx;
});
//Pathparser
var pp = new PathParser();
pp.add('hello/:id', function (ctx) {
return this.id;
});
pp.add('hello/world', function (ctx) {
return ctx;
});
pp.add('hello/jones', function (ctx) {
return ctx;
});
pp.add('hello/worlds', function (ctx) {
return ctx;
});
pp.add('by/:h3', function (ctx) {
return ctx.params.h3.length;
});
pp.add('by/h43', function (ctx) {
return ctx;
});
</script>
Ready to run.
Test | Ops/sec | |
---|---|---|
Rlite New |
| ready |
Rlite Old |
| ready |
Director |
| ready |
Backbone |
| ready |
Pagejs |
| ready |
Crossroads |
| ready |
PathParser |
| ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.