From b41ed29b80ea1454132e5707a978ad2ebd6b29a2 Mon Sep 17 00:00:00 2001 From: Brian White Date: Fri, 16 Mar 2018 20:35:32 -0400 Subject: [PATCH] stream: improve stream creation performance PR-URL: https://github.com/nodejs/node/pull/19401 Reviewed-By: Anna Henningsen Reviewed-By: Minwoo Jung Reviewed-By: Matteo Collina Reviewed-By: James M Snell Reviewed-By: Luigi Pinca Reviewed-By: Tiancheng "Timothy" Gu --- benchmark/streams/creation.js | 55 +++++++++++++++++++++++++ benchmark/streams/transform-creation.js | 21 ---------- lib/_stream_readable.js | 11 +++-- lib/_stream_writable.js | 16 ++++--- 4 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 benchmark/streams/creation.js delete mode 100644 benchmark/streams/transform-creation.js diff --git a/benchmark/streams/creation.js b/benchmark/streams/creation.js new file mode 100644 index 00000000000000..67187f91bd9cb1 --- /dev/null +++ b/benchmark/streams/creation.js @@ -0,0 +1,55 @@ +'use strict'; +const common = require('../common.js'); +const Duplex = require('stream').Duplex; +const Readable = require('stream').Readable; +const Transform = require('stream').Transform; +const Writable = require('stream').Writable; + +const bench = common.createBenchmark(main, { + n: [50e6], + kind: ['duplex', 'readable', 'transform', 'writable'] +}); + +function main({ n, kind }) { + var i = 0; + switch (kind) { + case 'duplex': + new Duplex({}); + new Duplex(); + + bench.start(); + for (; i < n; ++i) + new Duplex(); + bench.end(n); + break; + case 'readable': + new Readable({}); + new Readable(); + + bench.start(); + for (; i < n; ++i) + new Readable(); + bench.end(n); + break; + case 'writable': + new Writable({}); + new Writable(); + + bench.start(); + for (; i < n; ++i) + new Writable(); + bench.end(n); + break; + case 'transform': + new Transform({}); + new Transform(); + + bench.start(); + for (; i < n; ++i) + new Transform(); + bench.end(n); + break; + default: + throw new Error('Invalid kind'); + } +} diff --git a/benchmark/streams/transform-creation.js b/benchmark/streams/transform-creation.js deleted file mode 100644 index abfab0c8e25321..00000000000000 --- a/benchmark/streams/transform-creation.js +++ /dev/null @@ -1,21 +0,0 @@ -'use strict'; -const common = require('../common.js'); -const Transform = require('stream').Transform; -const inherits = require('util').inherits; - -const bench = common.createBenchmark(main, { - n: [1e6] -}); - -function MyTransform() { - Transform.call(this); -} -inherits(MyTransform, Transform); -MyTransform.prototype._transform = function() {}; - -function main({ n }) { - bench.start(); - for (var i = 0; i < n; ++i) - new MyTransform(); - bench.end(n); -} diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js index 9d475ab6eaa673..434dabd88378be 100644 --- a/lib/_stream_readable.js +++ b/lib/_stream_readable.js @@ -64,7 +64,7 @@ function prependListener(emitter, event, fn) { emitter._events[event] = [fn, emitter._events[event]]; } -function ReadableState(options, stream) { +function ReadableState(options, stream, isDuplex) { options = options || {}; // Duplex streams are both readable and writable, but share @@ -72,7 +72,8 @@ function ReadableState(options, stream) { // However, some cases require setting options to different // values for the readable and the writable sides of the duplex stream. // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Stream.Duplex; + if (typeof isDuplex !== 'boolean') + isDuplex = stream instanceof Stream.Duplex; // object stream flag. Used to make read(n) ignore n and to // make all the buffer merging and length checks go away @@ -142,7 +143,11 @@ function Readable(options) { if (!(this instanceof Readable)) return new Readable(options); - this._readableState = new ReadableState(options, this); + // Checking for a Stream.Duplex instance is faster here instead of inside + // the ReadableState constructor, at least with V8 6.5 + const isDuplex = (this instanceof Stream.Duplex); + + this._readableState = new ReadableState(options, this, isDuplex); // legacy this.readable = true; diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js index 5f9bf79ae41a26..5692cae1954008 100644 --- a/lib/_stream_writable.js +++ b/lib/_stream_writable.js @@ -48,7 +48,7 @@ util.inherits(Writable, Stream); function nop() {} -function WritableState(options, stream) { +function WritableState(options, stream, isDuplex) { options = options || {}; // Duplex streams are both readable and writable, but share @@ -56,7 +56,8 @@ function WritableState(options, stream) { // However, some cases require setting options to different // values for the readable and the writable sides of the duplex stream. // These options can be provided separately as readableXXX and writableXXX. - var isDuplex = stream instanceof Stream.Duplex; + if (typeof isDuplex !== 'boolean') + isDuplex = stream instanceof Stream.Duplex; // object stream flag to indicate whether or not this stream // contains buffers or objects. @@ -201,12 +202,15 @@ function Writable(options) { // Trying to use the custom `instanceof` for Writable here will also break the // Node.js LazyTransform implementation, which has a non-trivial getter for // `_writableState` that would lead to infinite recursion. - if (!(realHasInstance.call(Writable, this)) && - !(this instanceof Stream.Duplex)) { + + // Checking for a Stream.Duplex instance is faster here instead of inside + // the WritableState constructor, at least with V8 6.5 + const isDuplex = (this instanceof Stream.Duplex); + + if (!isDuplex && !realHasInstance.call(Writable, this)) return new Writable(options); - } - this._writableState = new WritableState(options, this); + this._writableState = new WritableState(options, this, isDuplex); // legacy. this.writable = true;