1 var assert = require('assert');
2 var path = require('path');
4 exports.PORT = process.env.PORT || 12346;
6 exports.createBenchmark = function(fn, options) {
7 return new Benchmark(fn, options);
10 function Benchmark(fn, options) {
12 this.options = options;
13 this.config = parseOpts(options);
14 this._name = path.basename(require.main.filename, '.js');
16 this._started = false;
18 process.nextTick(function() {
23 // run ab against a server.
24 Benchmark.prototype.ab = function(path, args, cb) {
25 var url = 'http://127.0.0.1:' + exports.PORT + path;
30 var spawn = require('child_process').spawn;
31 // console.error('ab %s', args.join(' '));
32 var child = spawn('ab', args);
34 child.stdout.setEncoding('utf8');
36 child.stdout.on('data', function(chunk) {
40 child.on('close', function(code) {
45 console.error('ab failed with ' + code);
48 var m = out.match(/Requests per second: +([0-9\.]+)/);
51 process.stderr.write(out + '\n');
52 console.error('ab produced strange output');
59 Benchmark.prototype._run = function() {
61 return this.fn(this.config);
63 // one more more options weren't set.
64 // run with all combinations
65 var main = require.main.filename;
68 var options = this.options;
70 var queue = Object.keys(options).reduce(function(set, key) {
71 var vals = options[key];
72 assert(Array.isArray(vals));
74 // match each item in the set with each item in the list
75 var newSet = new Array(set.length * vals.length);
77 set.forEach(function(s) {
78 vals.forEach(function(val) {
79 newSet[j++] = s.concat('--' + key + '=' + val);
85 var spawn = require('child_process').spawn;
86 var node = process.execPath;
89 var argv = queue[i++];
92 var child = spawn(node, argv, { stdio: 'inherit' });
93 child.on('close', function(code, signal) {
95 console.error('child process exited with code ' + code);
103 function parseOpts(options) {
104 // verify that there's an --option provided for each of the options
105 // if they're not *all* specified, then we return null.
106 var keys = Object.keys(options);
107 var num = keys.length;
109 for (var i = 2; i < process.argv.length; i++) {
110 var m = process.argv[i].match(/^--(.+)=(.+)$/);
111 if (!m || !m[1] || !m[2] || !options[m[1]])
114 conf[m[1]] = isFinite(m[2]) ? +m[2] : m[2]
118 return num === 0 ? conf : null;
121 Benchmark.prototype.start = function() {
123 throw new Error('Called start more than once in a single benchmark');
124 this._started = true;
125 this._start = process.hrtime();
128 Benchmark.prototype.end = function(operations) {
129 var elapsed = process.hrtime(this._start);
131 throw new Error('called end without start');
132 if (typeof operations !== 'number')
133 throw new Error('called end() without specifying operation count');
134 var time = elapsed[0] + elapsed[1]/1e9;
135 var rate = operations/time;
139 Benchmark.prototype.report = function(value) {
140 var heading = this.getHeading();
141 console.log('%s: %s', heading, value.toPrecision(5));
145 Benchmark.prototype.getHeading = function() {
146 var conf = this.config;
147 return this._name + '_' + Object.keys(conf).map(function(key) {
148 return key + '_' + conf[key];