diff --git a/benchmark/misc/console.js b/benchmark/misc/console.js new file mode 100644 index 00000000000000..d352943e80dd87 --- /dev/null +++ b/benchmark/misc/console.js @@ -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(); +} diff --git a/lib/console.js b/lib/console.js index 78c6955af54507..646e268624dd8f 100644 --- a/lib/console.js +++ b/lib/console.js @@ -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.warn = function() { - this._stderr.write(util.format.apply(null, arguments) + '\n'); +Console.prototype.warn = function(...args) { + 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) { - this._stdout.write(util.inspect(object, util._extend({ - customInspect: false - }, options)) + '\n'); + options = Object.assign({customInspect: false}, options); + this._stdout.write(`${util.inspect(object, options)}\n`); }; @@ -66,7 +69,7 @@ Console.prototype.time = function(label) { Console.prototype.timeEnd = function(label) { var time = this._times.get(label); if (!time) { - throw new Error('No such label: ' + label); + throw new Error(`No such label: ${label}`); } const duration = process.hrtime(time); 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 // exposed. var err = new Error(); err.name = 'Trace'; - err.message = util.format.apply(null, arguments); + err.message = util.format.apply(null, args); Error.captureStackTrace(err, trace); this.error(err.stack); }; -Console.prototype.assert = function(expression) { +Console.prototype.assert = function(expression, ...args) { if (!expression) { - const argsLen = arguments.length || 1; - 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)); + require('assert').ok(false, util.format.apply(null, args)); } };