bench: Add ab() method to common for http testing
[platform/upstream/nodejs.git] / benchmark / common.js
1 var assert = require('assert');
2 var path = require('path');
3
4 exports.PORT = process.env.PORT || 12346;
5
6 exports.createBenchmark = function(fn, options) {
7   return new Benchmark(fn, options);
8 };
9
10 function Benchmark(fn, options) {
11   this.fn = fn;
12   this.options = options;
13   this.config = parseOpts(options);
14   this._name = path.basename(require.main.filename, '.js');
15   this._start = [0,0];
16   this._started = false;
17   var self = this;
18   process.nextTick(function() {
19     self._run();
20   });
21 }
22
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;
26   args.push(url);
27
28   var self = this;
29   var out = '';
30   var spawn = require('child_process').spawn;
31   // console.error('ab %s', args.join(' '));
32   var child = spawn('ab', args);
33
34   child.stdout.setEncoding('utf8');
35
36   child.stdout.on('data', function(chunk) {
37     out += chunk;
38   });
39
40   child.on('close', function(code) {
41     if (cb)
42       cb(code);
43
44     if (code) {
45       console.error('ab failed with ' + code);
46       process.exit(code)
47     }
48     var m = out.match(/Requests per second: +([0-9\.]+)/);
49     var qps = m && +m[1];
50     if (!qps) {
51       process.stderr.write(out + '\n');
52       console.error('ab produced strange output');
53       process.exit(1);
54     }
55     self.report(+qps);
56   });
57 };
58
59 Benchmark.prototype._run = function() {
60   if (this.config)
61     return this.fn(this.config);
62
63   // one more more options weren't set.
64   // run with all combinations
65   var main = require.main.filename;
66   var settings = [];
67   var queueLen = 1;
68   var options = this.options;
69
70   var queue = Object.keys(options).reduce(function(set, key) {
71     var vals = options[key];
72     assert(Array.isArray(vals));
73
74     // match each item in the set with each item in the list
75     var newSet = new Array(set.length * vals.length);
76     var j = 0;
77     set.forEach(function(s) {
78       vals.forEach(function(val) {
79         newSet[j++] = s.concat('--' + key + '=' + val);
80       });
81     });
82     return newSet;
83   }, [[main]]);
84
85   var spawn = require('child_process').spawn;
86   var node = process.execPath;
87   var i = 0;
88   function run() {
89     var argv = queue[i++];
90     if (!argv)
91       return;
92     var child = spawn(node, argv, { stdio: 'inherit' });
93     child.on('close', function(code, signal) {
94       if (code)
95         console.error('child process exited with code ' + code);
96       else
97         run();
98     });
99   }
100   run();
101 };
102
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;
108   var conf = {};
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]])
112       return null;
113     else {
114       conf[m[1]] = isFinite(m[2]) ? +m[2] : m[2]
115       num--;
116     }
117   }
118   return num === 0 ? conf : null;
119 };
120
121 Benchmark.prototype.start = function() {
122   if (this._started)
123     throw new Error('Called start more than once in a single benchmark');
124   this._started = true;
125   this._start = process.hrtime();
126 };
127
128 Benchmark.prototype.end = function(operations) {
129   var elapsed = process.hrtime(this._start);
130   if (!this._started)
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;
136   this.report(rate);
137 };
138
139 Benchmark.prototype.report = function(value) {
140   var heading = this.getHeading();
141   console.log('%s: %s', heading, value.toPrecision(5));
142   process.exit(0);
143 };
144
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];
149   }).join('_');
150 }