node/benchmark/common.js
Rich Trott fb2f449acc
tools: update ESLint to current version
We have been stalled on ESLint 3.8.0 for some time. Current ESLint is
3.13.0. We have been unable to upgrade because of more aggressive
reporting on some rules, including indentation.

ESLint configuration options and bugfixes are now such that we can
reasonably upgrade.

PR-URL: https://github.com/nodejs/node/pull/10561
Reviewed-By: Teddy Katz <teddy.katz@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Sam Roberts <vieuxtech@gmail.com>
2017-03-08 17:11:24 -08:00

264 lines
6.7 KiB
JavaScript

'use strict';
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> [testFilter]');
process.exit(1);
}
var dir = path.join(__dirname, type);
var tests = fs.readdirSync(dir);
if (testFilter) {
var filteredTests = tests.filter(function(item) {
if (item.lastIndexOf(testFilter) >= 0) {
return item;
}
});
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 (outputFormat == 'default')
console.error(type + '/' + test);
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) {
return new Benchmark(fn, options);
};
function Benchmark(fn, options) {
this.fn = fn;
this.options = options;
this.config = parseOpts(options);
this._name = require.main.filename.split(/benchmark[/\\]/).pop();
this._start = [0, 0];
this._started = false;
var self = this;
process.nextTick(function() {
self._run();
});
}
// 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 child = child_process.spawn('wrk', args);
child.stdout.setEncoding('utf8');
child.stdout.on('data', function(chunk) {
out += chunk;
});
child.on('close', function(code) {
if (cb)
cb(code);
if (code) {
console.error('wrk failed with ' + code);
process.exit(code);
}
var match = out.match(regexp);
var qps = match && +match[1];
if (!qps) {
console.error('%j', out);
console.error('wrk produced strange output');
process.exit(1);
}
self.report(+qps);
});
};
Benchmark.prototype._run = function() {
if (this.config)
return this.fn(this.config);
// some options weren't set.
// run with all combinations
var main = require.main.filename;
var options = this.options;
var queue = Object.keys(options).reduce(function(set, key) {
var vals = options[key];
assert(Array.isArray(vals));
// match each item in the set with each item in the list
var newSet = new Array(set.length * vals.length);
var j = 0;
set.forEach(function(s) {
vals.forEach(function(val) {
if (typeof val !== 'number' && typeof val !== 'string') {
throw new TypeError(`configuration "${key}" had type ${typeof val}`);
}
newSet[j++] = s.concat(key + '=' + val);
});
});
return newSet;
}, [[main]]);
// 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;
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);
else
run();
});
}
run();
};
function parseOpts(options) {
// verify that there's an option provided for each of the options
// if they're not *all* specified, then we return null.
var keys = Object.keys(options);
var num = keys.length;
var conf = {};
for (var i = 2; i < process.argv.length; i++) {
var match = process.argv[i].match(/^(.+?)=([\s\S]*)$/);
if (!match || !match[1] || !options[match[1]]) {
return null;
} else {
conf[match[1]] = match[2];
num--;
}
}
// still go ahead and set whatever WAS set, if it was.
if (num !== 0) {
Object.keys(conf).forEach(function(k) {
options[k] = [conf[k]];
});
}
return num === 0 ? conf : null;
}
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);
};
Benchmark.prototype.report = function(value) {
var heading = this.getHeading();
if (outputFormat == 'default')
console.log('%s: %s', heading, value.toFixed(5));
else if (outputFormat == 'csv')
console.log('%s,%s', heading, value.toFixed(5));
};
Benchmark.prototype.getHeading = function() {
var conf = this.config;
if (outputFormat == 'default') {
return this._name + ' ' + Object.keys(conf).map(function(key) {
return key + '=' + JSON.stringify('' + conf[key]);
}).join(' ');
} else if (outputFormat == 'csv') {
return this._name + ',' + Object.keys(conf).map(function(key) {
return JSON.stringify('' + conf[key]);
}).join(',');
}
};
exports.v8ForceOptimization = function(method, ...args) {
if (typeof method !== 'function')
return;
const v8 = require('v8');
v8.setFlagsFromString('--allow_natives_syntax');
method.apply(null, args);
eval('%OptimizeFunctionOnNextCall(method)');
method.apply(null, args);
return eval('%GetOptimizationStatus(method)');
};