benchmark: pass execArgv to the benchmarking process
[platform/upstream/nodejs.git] / benchmark / common.js
1 var assert = require('assert');
2 var fs = require('fs');
3 var path = require('path');
4 var spawn = require('child_process').spawn;
5
6 var silent = +process.env.NODE_BENCH_SILENT;
7
8 exports.PORT = process.env.PORT || 12346;
9
10 // If this is the main module, then run the benchmarks
11 if (module === require.main) {
12   var type = process.argv[2];
13   var testFilter = process.argv[3];
14   if (!type) {
15     console.error('usage:\n ./iojs benchmark/common.js <type> [testFilter]');
16     process.exit(1);
17   }
18
19   var dir = path.join(__dirname, type);
20   var tests = fs.readdirSync(dir);
21
22   if (testFilter) {
23     var filteredTests = tests.filter(function(item){
24       if (item.lastIndexOf(testFilter) >= 0) {
25         return item;
26       }
27     });
28
29     if (filteredTests.length === 0) {
30       console.error('%s is not found in \n %j', testFilter, tests);
31       return;
32     }
33     tests = filteredTests;
34   }
35
36   runBenchmarks();
37 }
38
39 function runBenchmarks() {
40   var test = tests.shift();
41   if (!test)
42     return;
43
44   if (test.match(/^[\._]/))
45     return process.nextTick(runBenchmarks);
46
47   console.error(type + '/' + test);
48   test = path.resolve(dir, test);
49
50   var a = (process.execArgv || []).concat(test);
51   var child = spawn(process.execPath, a, { stdio: 'inherit' });
52   child.on('close', function(code) {
53     if (code) {
54       process.exit(code);
55     } else {
56       console.log('');
57       runBenchmarks();
58     }
59   });
60 }
61
62 exports.createBenchmark = function(fn, options) {
63   return new Benchmark(fn, options);
64 };
65
66 function Benchmark(fn, options) {
67   this.fn = fn;
68   this.options = options;
69   this.config = parseOpts(options);
70   this._name = require.main.filename.split(/benchmark[\/\\]/).pop();
71   this._start = [0,0];
72   this._started = false;
73   var self = this;
74   process.nextTick(function() {
75     self._run();
76   });
77 }
78
79 // benchmark an http server.
80 Benchmark.prototype.http = function(p, args, cb) {
81   var self = this;
82   var wrk = path.resolve(__dirname, '..', 'tools', 'wrk', 'wrk');
83   var regexp = /Requests\/sec:[ \t]+([0-9\.]+)/;
84   var url = 'http://127.0.0.1:' + exports.PORT + p;
85
86   args = args.concat(url);
87
88   var out = '';
89   var child = spawn(wrk, args);
90
91   child.stdout.setEncoding('utf8');
92
93   child.stdout.on('data', function(chunk) {
94     out += chunk;
95   });
96
97   child.on('close', function(code) {
98     if (cb)
99       cb(code);
100
101     if (code) {
102       console.error('wrk failed with ' + code);
103       process.exit(code)
104     }
105     var match = out.match(regexp);
106     var qps = match && +match[1];
107     if (!qps) {
108       console.error('%j', out);
109       console.error('wrk produced strange output');
110       process.exit(1);
111     }
112     self.report(+qps);
113   });
114 };
115
116 Benchmark.prototype._run = function() {
117   if (this.config)
118     return this.fn(this.config);
119
120   // one more more options weren't set.
121   // run with all combinations
122   var main = require.main.filename;
123   var settings = [];
124   var queueLen = 1;
125   var options = this.options;
126
127   var queue = Object.keys(options).reduce(function(set, key) {
128     var vals = options[key];
129     assert(Array.isArray(vals));
130
131     // match each item in the set with each item in the list
132     var newSet = new Array(set.length * vals.length);
133     var j = 0;
134     set.forEach(function(s) {
135       vals.forEach(function(val) {
136         newSet[j++] = s.concat(key + '=' + val);
137       });
138     });
139     return newSet;
140   }, [[main]]);
141
142   var node = process.execPath;
143   var i = 0;
144   function run() {
145     var argv = queue[i++];
146     if (!argv)
147       return;
148     argv = process.execArgv.concat(argv);
149     var child = spawn(node, argv, { stdio: 'inherit' });
150     child.on('close', function(code, signal) {
151       if (code)
152         console.error('child process exited with code ' + code);
153       else
154         run();
155     });
156   }
157   run();
158 };
159
160 function parseOpts(options) {
161   // verify that there's an option provided for each of the options
162   // if they're not *all* specified, then we return null.
163   var keys = Object.keys(options);
164   var num = keys.length;
165   var conf = {};
166   for (var i = 2; i < process.argv.length; i++) {
167     var match = process.argv[i].match(/^(.+)=(.+)$/);
168     if (!match || !match[1] || !match[2] || !options[match[1]]) {
169       return null;
170     } else {
171       conf[match[1]] = isFinite(match[2]) ? +match[2] : match[2]
172       num--;
173     }
174   }
175   // still go ahead and set whatever WAS set, if it was.
176   if (num !== 0) {
177     Object.keys(conf).forEach(function(k) {
178       options[k] = [conf[k]];
179     });
180   }
181   return num === 0 ? conf : null;
182 };
183
184 Benchmark.prototype.start = function() {
185   if (this._started)
186     throw new Error('Called start more than once in a single benchmark');
187
188   this._started = true;
189   this._start = process.hrtime();
190 };
191
192 Benchmark.prototype.end = function(operations) {
193   var elapsed = process.hrtime(this._start);
194
195   if (!this._started)
196     throw new Error('called end without start');
197   if (typeof operations !== 'number')
198     throw new Error('called end() without specifying operation count');
199
200   var time = elapsed[0] + elapsed[1]/1e9;
201   var rate = operations/time;
202   this.report(rate);
203 };
204
205 Benchmark.prototype.report = function(value) {
206   var heading = this.getHeading();
207   if (!silent)
208     console.log('%s: %s', heading, value.toFixed(5));
209
210   process.exit(0);
211 };
212
213 Benchmark.prototype.getHeading = function() {
214   var conf = this.config;
215   return this._name + ' ' + Object.keys(conf).map(function(key) {
216     return key + '=' + conf[key];
217   }).join(' ');
218 };