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