Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

stream,zlib: performance improvements #13322

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions benchmark/streams/transform-creation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
'use strict';
var common = require('../common.js');
var Transform = require('stream').Transform;
var inherits = require('util').inherits;

var bench = common.createBenchmark(main, {
n: [1e6]
});

function MyTransform() {
Transform.call(this);
}
inherits(MyTransform, Transform);
MyTransform.prototype._transform = function() {};

function main(conf) {
var n = +conf.n;

bench.start();
for (var i = 0; i < n; ++i)
new MyTransform();
bench.end(n);
}
32 changes: 32 additions & 0 deletions benchmark/zlib/creation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
'use strict';
var common = require('../common.js');
var zlib = require('zlib');

var bench = common.createBenchmark(main, {
type: [
'Deflate', 'DeflateRaw', 'Inflate', 'InflateRaw', 'Gzip', 'Gunzip', 'Unzip'
],
options: ['true', 'false'],
n: [5e5]
});

function main(conf) {
var n = +conf.n;
var fn = zlib['create' + conf.type];
if (typeof fn !== 'function')
throw new Error('Invalid zlib type');
var i = 0;

if (conf.options === 'true') {
var opts = {};
bench.start();
for (; i < n; ++i)
fn(opts);
bench.end(n);
} else {
bench.start();
for (; i < n; ++i)
fn();
bench.end(n);
}
}
54 changes: 54 additions & 0 deletions benchmark/zlib/deflate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
'use strict';
var common = require('../common.js');
var zlib = require('zlib');

var bench = common.createBenchmark(main, {
method: ['createDeflate', 'deflate', 'deflateSync'],
inputLen: [1024],
n: [4e5]
});

function main(conf) {
var n = +conf.n;
var method = conf.method;
var chunk = Buffer.alloc(+conf.inputLen, 'a');

var i = 0;
switch (method) {
// Performs `n` writes for a single deflate stream
case 'createDeflate':
var deflater = zlib.createDeflate();
deflater.resume();
deflater.on('finish', () => {
bench.end(n);
});

bench.start();
(function next() {
if (i++ === n)
return deflater.end();
deflater.write(chunk, next);
})();
break;
// Performs `n` single deflate operations
case 'deflate':
var deflate = zlib.deflate;
bench.start();
(function next(err, result) {
if (i++ === n)
return bench.end(n);
deflate(chunk, next);
})();
break;
// Performs `n` single deflateSync operations
case 'deflateSync':
var deflateSync = zlib.deflateSync;
bench.start();
for (; i < n; ++i)
deflateSync(chunk);
bench.end(n);
break;
default:
throw new Error('Unsupported deflate method');
}
}
67 changes: 30 additions & 37 deletions lib/_stream_transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,41 +70,29 @@ const util = require('util');
util.inherits(Transform, Duplex);


function TransformState(stream) {
this.afterTransform = function(er, data) {
return afterTransform(stream, er, data);
};

this.needTransform = false;
this.transforming = false;
this.writecb = null;
this.writechunk = null;
this.writeencoding = null;
}

function afterTransform(stream, er, data) {
var ts = stream._transformState;
function afterTransform(er, data) {
var ts = this._transformState;
ts.transforming = false;

var cb = ts.writecb;

if (!cb) {
return stream.emit('error',
new Error('write callback called multiple times'));
return this.emit('error',
new Error('write callback called multiple times'));
}

ts.writechunk = null;
ts.writecb = null;

if (data !== null && data !== undefined)
stream.push(data);
if (data != null) // single equals check for both `null` and `undefined`
this.push(data);

cb(er);

var rs = stream._readableState;
var rs = this._readableState;
rs.reading = false;
if (rs.needReadable || rs.length < rs.highWaterMark) {
stream._read(rs.highWaterMark);
this._read(rs.highWaterMark);
}
}

Expand All @@ -115,9 +103,14 @@ function Transform(options) {

Duplex.call(this, options);

this._transformState = new TransformState(this);

var stream = this;
this._transformState = {
afterTransform: afterTransform.bind(this),
needTransform: false,
transforming: false,
writecb: null,
writechunk: null,
writeencoding: null
};

// start out asking for a readable event once data is transformed.
this._readableState.needReadable = true;
Expand All @@ -136,14 +129,17 @@ function Transform(options) {
}

// When the writable side finishes, then flush out anything remaining.
this.once('prefinish', function() {
if (typeof this._flush === 'function')
this._flush(function(er, data) {
done(stream, er, data);
});
else
done(stream);
});
this.on('prefinish', prefinish);
}

function prefinish() {
if (typeof this._flush === 'function') {
this._flush((er, data) => {
done(this, er, data);
});
} else {
done(this, null, null);
}
}

Transform.prototype.push = function(chunk, encoding) {
Expand Down Expand Up @@ -208,18 +204,15 @@ function done(stream, er, data) {
if (er)
return stream.emit('error', er);

if (data !== null && data !== undefined)
if (data != null) // single equals check for both `null` and `undefined`
stream.push(data);

// if there's nothing in the write buffer, then that means
// that nothing more will ever be provided
var ws = stream._writableState;
var ts = stream._transformState;

if (ws.length)
if (stream._writableState.length)
throw new Error('Calling transform done when ws.length != 0');

if (ts.transforming)
if (stream._transformState.transforming)
throw new Error('Calling transform done when still transforming');

return stream.push(null);
Expand Down
Loading