var assert = require('assert');
+var fs = require('fs');
var path = require('path');
+var child_process = require('child_process');
+
+var outputFormat = process.env.OUTPUT_FORMAT ||
+ (+process.env.NODE_BENCH_SILENT ? 'silent' : false) ||
+ 'default';
+
+// verify outputFormat
+if (['default', 'csv', 'silent'].indexOf(outputFormat) == -1) {
+ throw new Error('OUTPUT_FORMAT set to invalid value');
+}
exports.PORT = process.env.PORT || 12346;
// If this is the main module, then run the benchmarks
if (module === require.main) {
var type = process.argv[2];
+ var testFilter = process.argv[3];
if (!type) {
- console.error('usage:\n ./node benchmark/common.js <type>');
+ console.error('usage:\n ./node benchmark/common.js <type> [testFilter]');
process.exit(1);
}
- var path = require('path');
- var fs = require('fs');
var dir = path.join(__dirname, type);
var tests = fs.readdirSync(dir);
- var spawn = require('child_process').spawn;
- runBenchmarks();
+ if (testFilter) {
+ var filteredTests = tests.filter(function(item){
+ if (item.lastIndexOf(testFilter) >= 0) {
+ return item;
+ }
+ });
- function runBenchmarks() {
- var test = tests.shift();
- if (!test)
+ if (filteredTests.length === 0) {
+ console.error('%s is not found in \n %j', testFilter, tests);
return;
+ }
+ tests = filteredTests;
+ }
+
+ runBenchmarks();
+}
+
+function hasWrk() {
+ var result = child_process.spawnSync('wrk', ['-h']);
+ if (result.error && result.error.code === 'ENOENT') {
+ console.error('Couldn\'t locate `wrk` which is needed for running ' +
+ 'benchmarks. Check benchmark/README.md for further instructions.');
+ process.exit(-1);
+ }
+}
+
+function runBenchmarks() {
+ var test = tests.shift();
+ if (!test)
+ return;
- if (test.match(/^[\._]/))
- return process.nextTick(runBenchmarks);
+ if (test.match(/^[\._]/))
+ return process.nextTick(runBenchmarks);
+ if (outputFormat == 'default')
console.error(type + '/' + test);
- test = path.resolve(dir, test);
- var child = spawn(process.execPath, [ test ], { stdio: 'inherit' });
- child.on('close', function(code) {
- if (code)
- process.exit(code);
- else {
- console.log('');
- runBenchmarks();
- }
- });
- }
+ test = path.resolve(dir, test);
+
+ var a = (process.execArgv || []).concat(test);
+ var child = child_process.spawn(process.execPath, a, { stdio: 'inherit' });
+ child.on('close', function(code) {
+ if (code) {
+ process.exit(code);
+ } else {
+ console.log('');
+ runBenchmarks();
+ }
+ });
}
exports.createBenchmark = function(fn, options) {
this._name = require.main.filename.split(/benchmark[\/\\]/).pop();
this._start = [0,0];
this._started = false;
+
var self = this;
+
process.nextTick(function() {
self._run();
});
}
-// run ab against a server.
-Benchmark.prototype.ab = function(path, args, cb) {
- var url = 'http://127.0.0.1:' + exports.PORT + path;
- args.push(url);
-
+// benchmark an http server.
+Benchmark.prototype.http = function(p, args, cb) {
+ hasWrk();
var self = this;
+ var regexp = /Requests\/sec:[ \t]+([0-9\.]+)/;
+ var url = 'http://127.0.0.1:' + exports.PORT + p;
+
+ args = args.concat(url);
+
var out = '';
- var spawn = require('child_process').spawn;
- // console.error('ab %s', args.join(' '));
- var child = spawn('ab', args);
+ var child = child_process.spawn('wrk', args);
child.stdout.setEncoding('utf8');
cb(code);
if (code) {
- console.error('ab failed with ' + code);
+ console.error('wrk failed with ' + code);
process.exit(code)
}
- var m = out.match(/Requests per second: +([0-9\.]+)/);
- var qps = m && +m[1];
+ var match = out.match(regexp);
+ var qps = match && +match[1];
if (!qps) {
- process.stderr.write(out + '\n');
- console.error('ab produced strange output');
+ console.error('%j', out);
+ console.error('wrk produced strange output');
process.exit(1);
}
self.report(+qps);
if (this.config)
return this.fn(this.config);
- // one more more options weren't set.
+ // some options weren't set.
// run with all combinations
var main = require.main.filename;
var settings = [];
return newSet;
}, [[main]]);
- var spawn = require('child_process').spawn;
+ // output csv heading
+ if (outputFormat == 'csv')
+ console.log('filename,' + Object.keys(options).join(',') + ',result');
+
var node = process.execPath;
var i = 0;
function run() {
var argv = queue[i++];
if (!argv)
return;
- var child = spawn(node, argv, { stdio: 'inherit' });
+ argv = process.execArgv.concat(argv);
+ var child = child_process.spawn(node, argv, { stdio: 'inherit' });
child.on('close', function(code, signal) {
if (code)
console.error('child process exited with code ' + code);
var num = keys.length;
var conf = {};
for (var i = 2; i < process.argv.length; i++) {
- var m = process.argv[i].match(/^(.+)=(.+)$/);
- if (!m || !m[1] || !m[2] || !options[m[1]])
+ var match = process.argv[i].match(/^(.+)=(.+)$/);
+ if (!match || !match[1] || !match[2] || !options[match[1]]) {
return null;
- else {
- conf[m[1]] = isFinite(m[2]) ? +m[2] : m[2]
+ } else {
+ conf[match[1]] = isFinite(match[2]) ? +match[2] : match[2]
num--;
}
}
Benchmark.prototype.start = function() {
if (this._started)
throw new Error('Called start more than once in a single benchmark');
+
this._started = true;
this._start = process.hrtime();
};
Benchmark.prototype.end = function(operations) {
var elapsed = process.hrtime(this._start);
+
if (!this._started)
throw new Error('called end without start');
if (typeof operations !== 'number')
throw new Error('called end() without specifying operation count');
+
var time = elapsed[0] + elapsed[1]/1e9;
var rate = operations/time;
this.report(rate);
Benchmark.prototype.report = function(value) {
var heading = this.getHeading();
- console.log('%s: %s', heading, value.toPrecision(5));
+
+ if (outputFormat == 'default')
+ console.log('%s: %s', heading, value.toFixed(5));
+ else if (outputFormat == 'csv')
+ console.log('%s,%s', heading, value.toFixed(5));
+
process.exit(0);
};
Benchmark.prototype.getHeading = function() {
var conf = this.config;
- return this._name + ' ' + Object.keys(conf).map(function(key) {
- return key + '=' + conf[key];
- }).join(' ');
-}
+
+ if (outputFormat == 'default') {
+ return this._name + ' ' + Object.keys(conf).map(function(key) {
+ return key + '=' + conf[key];
+ }).join(' ');
+ } else if (outputFormat == 'csv') {
+ return this._name + ',' + Object.keys(conf).map(function(key) {
+ return conf[key];
+ }).join(',');
+ }
+};