mirror of https://github.com/nodejs/node.git
console: refactor to use rest params and template strings
Overall cleanup in code, eliminate reliance on `arguments`. Benchmarks show that as of v8 5.0.71.32, using rest params + apply has good performance. The spread operator is not yet well optimized in v8 ``` misc/console.js method=restAndSpread concat=1 n=1000000: 374779.38359 misc/console.js method=restAndSpread concat=0 n=1000000: 375988.30434 misc/console.js method=argumentsAndApply concat=1 n=1000000: 682618.61125 misc/console.js method=argumentsAndApply concat=0 n=1000000: 645093.74443 misc/console.js method=restAndApply concat=1 n=1000000: 682931.41217 misc/console.js method=restAndApply concat=0 n=1000000: 664473.09700 ``` PR-URL: https://github.com/nodejs/node/pull/6233 Reviewed-By: Brian White <mscdex@mscdex.net>
This commit is contained in:
parent
e7c077c610
commit
33c242e7e9
|
@ -0,0 +1,133 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const common = require('../common.js');
|
||||||
|
const assert = require('assert');
|
||||||
|
const Writable = require('stream').Writable;
|
||||||
|
const util = require('util');
|
||||||
|
const v8 = require('v8');
|
||||||
|
|
||||||
|
v8.setFlagsFromString('--allow_natives_syntax');
|
||||||
|
|
||||||
|
var bench = common.createBenchmark(main, {
|
||||||
|
method: ['restAndSpread',
|
||||||
|
'argumentsAndApply',
|
||||||
|
'restAndApply',
|
||||||
|
'restAndConcat'],
|
||||||
|
concat: [1, 0],
|
||||||
|
n: [1000000]
|
||||||
|
});
|
||||||
|
|
||||||
|
const nullStream = createNullStream();
|
||||||
|
|
||||||
|
function usingRestAndConcat(...args) {
|
||||||
|
nullStream.write('this is ' + args[0] + ' of ' + args[1] + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function usingRestAndSpreadTS(...args) {
|
||||||
|
nullStream.write(`${util.format(...args)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function usingRestAndApplyTS(...args) {
|
||||||
|
nullStream.write(`${util.format.apply(null, args)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function usingArgumentsAndApplyTS() {
|
||||||
|
nullStream.write(`${util.format.apply(null, arguments)}\n`);
|
||||||
|
}
|
||||||
|
|
||||||
|
function usingRestAndSpreadC(...args) {
|
||||||
|
nullStream.write(util.format(...args) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function usingRestAndApplyC(...args) {
|
||||||
|
nullStream.write(util.format.apply(null, args) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function usingArgumentsAndApplyC() {
|
||||||
|
nullStream.write(util.format.apply(null, arguments) + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
function optimize(method, ...args) {
|
||||||
|
method(...args);
|
||||||
|
eval(`%OptimizeFunctionOnNextCall(${method.name})`);
|
||||||
|
method(...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runUsingRestAndConcat(n) {
|
||||||
|
optimize(usingRestAndConcat, 'a', 1);
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
bench.start();
|
||||||
|
for (; i < n; i++)
|
||||||
|
usingRestAndConcat('a', 1);
|
||||||
|
bench.end(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runUsingRestAndSpread(n, concat) {
|
||||||
|
|
||||||
|
const method = concat ? usingRestAndSpreadC : usingRestAndSpreadTS;
|
||||||
|
optimize(method, 'this is %s of %d', 'a', 1);
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
bench.start();
|
||||||
|
for (; i < n; i++)
|
||||||
|
method('this is %s of %d', 'a', 1);
|
||||||
|
bench.end(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runUsingRestAndApply(n, concat) {
|
||||||
|
|
||||||
|
const method = concat ? usingRestAndApplyC : usingRestAndApplyTS;
|
||||||
|
optimize(method, 'this is %s of %d', 'a', 1);
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
bench.start();
|
||||||
|
for (; i < n; i++)
|
||||||
|
method('this is %s of %d', 'a', 1);
|
||||||
|
bench.end(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runUsingArgumentsAndApply(n, concat) {
|
||||||
|
|
||||||
|
const method = concat ? usingArgumentsAndApplyC : usingArgumentsAndApplyTS;
|
||||||
|
optimize(method, 'this is %s of %d', 'a', 1);
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
bench.start();
|
||||||
|
for (; i < n; i++)
|
||||||
|
method('this is %s of %d', 'a', 1);
|
||||||
|
bench.end(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(conf) {
|
||||||
|
const n = +conf.n;
|
||||||
|
switch (conf.method) {
|
||||||
|
case 'restAndSpread':
|
||||||
|
runUsingRestAndSpread(n, conf.concat);
|
||||||
|
break;
|
||||||
|
case 'restAndApply':
|
||||||
|
runUsingRestAndApply(n, conf.concat);
|
||||||
|
break;
|
||||||
|
case 'argumentsAndApply':
|
||||||
|
runUsingArgumentsAndApply(n, conf.concat);
|
||||||
|
break;
|
||||||
|
case 'restAndConcat':
|
||||||
|
if (conf.concat)
|
||||||
|
runUsingRestAndConcat(n);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error('Unexpected method');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function createNullStream() {
|
||||||
|
// Used to approximate /dev/null
|
||||||
|
function NullStream() {
|
||||||
|
Writable.call(this, {});
|
||||||
|
}
|
||||||
|
util.inherits(NullStream, Writable);
|
||||||
|
NullStream.prototype._write = function(cb) {
|
||||||
|
assert.strictEqual(cb.toString(), 'this is a of 1\n');
|
||||||
|
};
|
||||||
|
return new NullStream();
|
||||||
|
}
|
|
@ -35,16 +35,20 @@ function Console(stdout, stderr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.prototype.log = function() {
|
|
||||||
this._stdout.write(util.format.apply(null, arguments) + '\n');
|
// As of v8 5.0.71.32, the combination of rest param, template string
|
||||||
|
// and .apply(null, args) benchmarks consistently faster than using
|
||||||
|
// the spread operator when calling util.format.
|
||||||
|
Console.prototype.log = function(...args) {
|
||||||
|
this._stdout.write(`${util.format.apply(null, args)}\n`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.info = Console.prototype.log;
|
Console.prototype.info = Console.prototype.log;
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.warn = function() {
|
Console.prototype.warn = function(...args) {
|
||||||
this._stderr.write(util.format.apply(null, arguments) + '\n');
|
this._stderr.write(`${util.format.apply(null, args)}\n`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,9 +56,8 @@ Console.prototype.error = Console.prototype.warn;
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.dir = function(object, options) {
|
Console.prototype.dir = function(object, options) {
|
||||||
this._stdout.write(util.inspect(object, util._extend({
|
options = Object.assign({customInspect: false}, options);
|
||||||
customInspect: false
|
this._stdout.write(`${util.inspect(object, options)}\n`);
|
||||||
}, options)) + '\n');
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -66,7 +69,7 @@ Console.prototype.time = function(label) {
|
||||||
Console.prototype.timeEnd = function(label) {
|
Console.prototype.timeEnd = function(label) {
|
||||||
var time = this._times.get(label);
|
var time = this._times.get(label);
|
||||||
if (!time) {
|
if (!time) {
|
||||||
throw new Error('No such label: ' + label);
|
throw new Error(`No such label: ${label}`);
|
||||||
}
|
}
|
||||||
const duration = process.hrtime(time);
|
const duration = process.hrtime(time);
|
||||||
const ms = duration[0] * 1000 + duration[1] / 1e6;
|
const ms = duration[0] * 1000 + duration[1] / 1e6;
|
||||||
|
@ -75,24 +78,20 @@ Console.prototype.timeEnd = function(label) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.trace = function trace() {
|
Console.prototype.trace = function trace(...args) {
|
||||||
// TODO probably can to do this better with V8's debug object once that is
|
// TODO probably can to do this better with V8's debug object once that is
|
||||||
// exposed.
|
// exposed.
|
||||||
var err = new Error();
|
var err = new Error();
|
||||||
err.name = 'Trace';
|
err.name = 'Trace';
|
||||||
err.message = util.format.apply(null, arguments);
|
err.message = util.format.apply(null, args);
|
||||||
Error.captureStackTrace(err, trace);
|
Error.captureStackTrace(err, trace);
|
||||||
this.error(err.stack);
|
this.error(err.stack);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
Console.prototype.assert = function(expression) {
|
Console.prototype.assert = function(expression, ...args) {
|
||||||
if (!expression) {
|
if (!expression) {
|
||||||
const argsLen = arguments.length || 1;
|
require('assert').ok(false, util.format.apply(null, args));
|
||||||
const arr = new Array(argsLen - 1);
|
|
||||||
for (var i = 1; i < argsLen; i++)
|
|
||||||
arr[i - 1] = arguments[i];
|
|
||||||
require('assert').ok(false, util.format.apply(null, arr));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue