| var assert = require('assert'); |
| var path = require('path'); |
| var silent = +process.env.NODE_BENCH_SILENT; |
| |
| 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]; |
| if (!type) { |
| console.error('usage:\n ./node benchmark/common.js <type>'); |
| process.exit(1); |
| } |
| |
| var fs = require('fs'); |
| var dir = path.join(__dirname, type); |
| var tests = fs.readdirSync(dir); |
| var spawn = require('child_process').spawn; |
| |
| runBenchmarks(); |
| } |
| |
| function runBenchmarks() { |
| var test = tests.shift(); |
| if (!test) |
| return; |
| |
| if (test.match(/^[\._]/)) |
| return process.nextTick(runBenchmarks); |
| |
| console.error(type + '/' + test); |
| test = path.resolve(dir, test); |
| |
| var a = (process.execArgv || []).concat(test); |
| var child = 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) { |
| return new Benchmark(fn, options); |
| }; |
| |
| function Benchmark(fn, options) { |
| this.fn = fn; |
| this.options = options; |
| this.config = parseOpts(options); |
| this._name = require.main.filename.split(/benchmark[\/\\]/).pop(); |
| this._start = [0,0]; |
| this._started = false; |
| var self = this; |
| process.nextTick(function() { |
| self._run(); |
| }); |
| } |
| |
| // benchmark an http server. |
| Benchmark.prototype.http = function(p, args, cb) { |
| var self = this; |
| var wrk = path.resolve(__dirname, '..', 'tools', 'wrk', 'wrk'); |
| var regexp = /Requests\/sec:[ \t]+([0-9\.]+)/; |
| var spawn = require('child_process').spawn; |
| var url = 'http://127.0.0.1:' + exports.PORT + p; |
| |
| args = args.concat(url); |
| |
| var out = ''; |
| var child = spawn(wrk, args); |
| |
| child.stdout.setEncoding('utf8'); |
| |
| child.stdout.on('data', function(chunk) { |
| out += chunk; |
| }); |
| |
| child.on('close', function(code) { |
| if (cb) |
| cb(code); |
| |
| if (code) { |
| console.error('wrk failed with ' + code); |
| process.exit(code) |
| } |
| var m = out.match(regexp); |
| var qps = m && +m[1]; |
| if (!qps) { |
| console.error('%j', out); |
| console.error('wrk produced strange output'); |
| process.exit(1); |
| } |
| self.report(+qps); |
| }); |
| }; |
| |
| Benchmark.prototype._run = function() { |
| if (this.config) |
| return this.fn(this.config); |
| |
| // one more more options weren't set. |
| // run with all combinations |
| var main = require.main.filename; |
| var settings = []; |
| var queueLen = 1; |
| var options = this.options; |
| |
| var queue = Object.keys(options).reduce(function(set, key) { |
| var vals = options[key]; |
| assert(Array.isArray(vals)); |
| |
| // match each item in the set with each item in the list |
| var newSet = new Array(set.length * vals.length); |
| var j = 0; |
| set.forEach(function(s) { |
| vals.forEach(function(val) { |
| newSet[j++] = s.concat(key + '=' + val); |
| }); |
| }); |
| return newSet; |
| }, [[main]]); |
| |
| var spawn = require('child_process').spawn; |
| var node = process.execPath; |
| var i = 0; |
| function run() { |
| var argv = queue[i++]; |
| if (!argv) |
| return; |
| var child = spawn(node, argv, { stdio: 'inherit' }); |
| child.on('close', function(code, signal) { |
| if (code) |
| console.error('child process exited with code ' + code); |
| else |
| run(); |
| }); |
| } |
| run(); |
| }; |
| |
| function parseOpts(options) { |
| // verify that there's an option provided for each of the options |
| // if they're not *all* specified, then we return null. |
| var keys = Object.keys(options); |
| 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]]) |
| return null; |
| else { |
| conf[m[1]] = isFinite(m[2]) ? +m[2] : m[2] |
| num--; |
| } |
| } |
| // still go ahead and set whatever WAS set, if it was. |
| if (num !== 0) { |
| Object.keys(conf).forEach(function(k) { |
| options[k] = [conf[k]]; |
| }); |
| } |
| return num === 0 ? conf : null; |
| }; |
| |
| 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(); |
| if (!silent) |
| console.log('%s: %s', heading, value.toPrecision(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(' '); |
| } |