doc: document directories in test directory
[platform/upstream/nodejs.git] / benchmark / common.js
index 289f17d..511ae38 100644 (file)
@@ -1,45 +1,80 @@
 var assert = require('assert');
+var fs = require('fs');
 var path = require('path');
+var child_process = require('child_process');
+
+var outputFormat = process.env.OUTPUT_FORMAT ||
+                   (+process.env.NODE_BENCH_SILENT ? 'silent' : false) ||
+                   'default';
+
+// verify outputFormat
+if (['default', 'csv', 'silent'].indexOf(outputFormat) == -1) {
+  throw new Error('OUTPUT_FORMAT set to invalid value');
+}
 
 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];
+  var testFilter = process.argv[3];
   if (!type) {
-    console.error('usage:\n ./node benchmark/common.js <type>');
+    console.error('usage:\n ./node benchmark/common.js <type> [testFilter]');
     process.exit(1);
   }
 
-  var path = require('path');
-  var fs = require('fs');
   var dir = path.join(__dirname, type);
   var tests = fs.readdirSync(dir);
-  var spawn = require('child_process').spawn;
 
-  runBenchmarks();
+  if (testFilter) {
+    var filteredTests = tests.filter(function(item){
+      if (item.lastIndexOf(testFilter) >= 0) {
+        return item;
+      }
+    });
 
-  function runBenchmarks() {
-    var test = tests.shift();
-    if (!test)
+    if (filteredTests.length === 0) {
+      console.error('%s is not found in \n %j', testFilter, tests);
       return;
+    }
+    tests = filteredTests;
+  }
+
+  runBenchmarks();
+}
+
+function hasWrk() {
+  var result = child_process.spawnSync('wrk', ['-h']);
+  if (result.error && result.error.code === 'ENOENT') {
+    console.error('Couldn\'t locate `wrk` which is needed for running ' +
+      'benchmarks. Check benchmark/README.md for further instructions.');
+      process.exit(-1);
+  }
+}
+
+function runBenchmarks() {
+  var test = tests.shift();
+  if (!test)
+    return;
 
-    if (test.match(/^[\._]/))
-      return process.nextTick(runBenchmarks);
+  if (test.match(/^[\._]/))
+    return process.nextTick(runBenchmarks);
 
+  if (outputFormat == 'default')
     console.error(type + '/' + test);
-    test = path.resolve(dir, test);
 
-    var child = spawn(process.execPath, [ test ], { stdio: 'inherit' });
-    child.on('close', function(code) {
-      if (code)
-        process.exit(code);
-      else {
-        console.log('');
-        runBenchmarks();
-      }
-    });
-  }
+  test = path.resolve(dir, test);
+
+  var a = (process.execArgv || []).concat(test);
+  var child = child_process.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) {
@@ -53,22 +88,25 @@ function Benchmark(fn, options) {
   this._name = require.main.filename.split(/benchmark[\/\\]/).pop();
   this._start = [0,0];
   this._started = false;
+
   var self = this;
+
   process.nextTick(function() {
     self._run();
   });
 }
 
-// run ab against a server.
-Benchmark.prototype.ab = function(path, args, cb) {
-  var url = 'http://127.0.0.1:' + exports.PORT + path;
-  args.push(url);
-
+// benchmark an http server.
+Benchmark.prototype.http = function(p, args, cb) {
+  hasWrk();
   var self = this;
+  var regexp = /Requests\/sec:[ \t]+([0-9\.]+)/;
+  var url = 'http://127.0.0.1:' + exports.PORT + p;
+
+  args = args.concat(url);
+
   var out = '';
-  var spawn = require('child_process').spawn;
-  // console.error('ab %s', args.join(' '));
-  var child = spawn('ab', args);
+  var child = child_process.spawn('wrk', args);
 
   child.stdout.setEncoding('utf8');
 
@@ -81,14 +119,14 @@ Benchmark.prototype.ab = function(path, args, cb) {
       cb(code);
 
     if (code) {
-      console.error('ab failed with ' + code);
+      console.error('wrk failed with ' + code);
       process.exit(code)
     }
-    var m = out.match(/Requests per second: +([0-9\.]+)/);
-    var qps = m && +m[1];
+    var match = out.match(regexp);
+    var qps = match && +match[1];
     if (!qps) {
-      process.stderr.write(out + '\n');
-      console.error('ab produced strange output');
+      console.error('%j', out);
+      console.error('wrk produced strange output');
       process.exit(1);
     }
     self.report(+qps);
@@ -99,7 +137,7 @@ Benchmark.prototype._run = function() {
   if (this.config)
     return this.fn(this.config);
 
-  // one more more options weren't set.
+  // some options weren't set.
   // run with all combinations
   var main = require.main.filename;
   var settings = [];
@@ -121,14 +159,18 @@ Benchmark.prototype._run = function() {
     return newSet;
   }, [[main]]);
 
-  var spawn = require('child_process').spawn;
+  // output csv heading
+  if (outputFormat == 'csv')
+    console.log('filename,' + Object.keys(options).join(',') + ',result');
+
   var node = process.execPath;
   var i = 0;
   function run() {
     var argv = queue[i++];
     if (!argv)
       return;
-    var child = spawn(node, argv, { stdio: 'inherit' });
+    argv = process.execArgv.concat(argv);
+    var child = child_process.spawn(node, argv, { stdio: 'inherit' });
     child.on('close', function(code, signal) {
       if (code)
         console.error('child process exited with code ' + code);
@@ -146,11 +188,11 @@ function parseOpts(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]])
+    var match = process.argv[i].match(/^(.+)=(.+)$/);
+    if (!match || !match[1] || !match[2] || !options[match[1]]) {
       return null;
-    else {
-      conf[m[1]] = isFinite(m[2]) ? +m[2] : m[2]
+    else {
+      conf[match[1]] = isFinite(match[2]) ? +match[2] : match[2]
       num--;
     }
   }
@@ -166,16 +208,19 @@ function parseOpts(options) {
 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);
@@ -183,13 +228,25 @@ Benchmark.prototype.end = function(operations) {
 
 Benchmark.prototype.report = function(value) {
   var heading = this.getHeading();
-  console.log('%s: %s', heading, value.toPrecision(5));
+
+  if (outputFormat == 'default')
+    console.log('%s: %s', heading, value.toFixed(5));
+  else if (outputFormat == 'csv')
+    console.log('%s,%s', heading, value.toFixed(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(' ');
-}
+
+  if (outputFormat == 'default') {
+    return this._name + ' ' + Object.keys(conf).map(function(key) {
+      return key + '=' + conf[key];
+    }).join(' ');
+  } else if (outputFormat == 'csv') {
+    return this._name + ',' + Object.keys(conf).map(function(key) {
+      return conf[key];
+    }).join(',');
+  }
+};