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: skip chunk validation for Buffers #10580

Merged
merged 1 commit into from
Jan 5, 2017

Conversation

mscdex
Copy link
Contributor

@mscdex mscdex commented Jan 2, 2017

These changes result in ~50% improvement in the included benchmark:

                                          improvement significant      p.value
 streams/writable-manywrites.js n=2000000     52.38 %         *** 3.778963e-47

/cc @nodejs/streams
CI: https://ci.nodejs.org/job/node-test-pull-request/5668/

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • commit message follows commit guidelines
Affected core subsystem(s)
  • stream

@mscdex mscdex added performance Issues and PRs related to the performance of Node.js. stream Issues and PRs related to the stream subsystem. labels Jan 2, 2017
@Fishrock123
Copy link
Contributor

instanceof should be very fast, is there any way to inspect why this speeds it up that much?

Copy link
Member

@ChALkeR ChALkeR left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM after CI passes.

@mscdex
Copy link
Contributor Author

mscdex commented Jan 3, 2017

@Fishrock123 Actually, instanceof is usually quite slow from my experience, even in V8 5.4. The InstanceOf V8 C++ function is usually one of the top few functions that consume the most ticks when profiling anything that uses (Writable) streams.

It's also avoiding a function call in case validChunk doesn't get inlined for some reason (most likely due to reaching max inline depth), so there is that too.

@mscdex
Copy link
Contributor Author

mscdex commented Jan 3, 2017

Added another optimization that provides an additional ~30% increase.

CI again: https://ci.nodejs.org/job/node-test-pull-request/5669/

@italoacasas
Copy link
Contributor

@nodejs/streams

@Fishrock123
Copy link
Contributor

Oh, maybe I was thinking about typeof.

if (chunk instanceof Buffer)
encoding = 'buffer';
function writeOrBuffer(stream, state, isBuf, chunk, encoding, cb) {
if (!isBuf) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mscdex could chunk have changed before this is called?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. writeOrBuffer() is only called by write() and validChunk() does not return a modified chunk or anything.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code in itself is fine, and it's a very good catch. However the title in the PR and the commit message does not match the content.
The check is not skipped, it is just executed once.

// and we're not in objectMode, then that's an error.
// Otherwise stream chunks are all considered to be of length=1, and the
// watermarks determine how many objects to keep in the buffer, rather than
// how many bytes or characters.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add back some comment here, explaining this change. validChunk  now only checks if a chunk is valid when it is not a Buffer.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the comments here because they either duplicated what the inline comments already said or had nothing to do with validChunk() ('Otherwise stream chunks ...').

Copy link
Contributor Author

@mscdex mscdex Jan 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even the inline comments are pretty redundant as they just explain what the simple code below it is doing, not worth keeping IMHO. I have added a new comment above the function definition now.

@mscdex
Copy link
Contributor Author

mscdex commented Jan 3, 2017

The check is not skipped, it is just executed once.

I'm not sure what you mean by this. validChunk() is skipped when chunk is a Buffer.

@mcollina
Copy link
Member

mcollina commented Jan 3, 2017

I'm not sure what you mean by this. validChunk() is skipped when chunk is a Buffer.

I mean that the chunk is validated, i.e. chunk instanceof Buffer is executed, just not on the validChunk method.


function main(conf) {
const n = +conf.n;
const b = new Buffer(1024);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is going to be kept as new Buffer(...), can you add a comment explaining why (e.g. to allow comparison with older Node.js versions). That said, I'd prefer this to be changed to Buffer.alloc(...) given that Node.js 4 is the lowest currently supported version and the new constructors have been backported to that version already.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

typeof chunk !== 'string' &&
chunk !== undefined &&
!state.objectMode) {
} else if (typeof chunk !== 'string' &&
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would switching to Buffer.isBuffer() have the same performance profile as instanceof? By removing the instanceof Buffer check here, this becomes a semver-major. If we can switch to Buffer.isBuffer() and still get a performance boost, then this can be semver-patch.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jasnell this PR is not removing the check (see my comment). It is still doing it, just in a different way. I would flag it semver-minor, and it can even be backported to v4 and v6.

Copy link
Contributor Author

@mscdex mscdex Jan 3, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Buffer.isBuffer() is just an instanceof check, so that would not help. Also, @mcollina is correct, we're just avoiding duplicate instanceofs since it was already being done in write().

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah.. lol, forgot about that :-)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mscdex that's my point on the review. "remove duplicate instanceof checks" should be a good commit message/pr title.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mcollina It's not just that though, for example it would have also checked for null before getting to the second instanceof check. Anyway, I've changed the commit message. Let me know if it's more suitable.


function main(conf) {
const n = +conf.n;
const b = new Buffer(1024);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use Buffer.alloc or Buffer.allocUnsafe here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just copied from one of the other buffer benchmarks.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.

Copy link
Member

@mcollina mcollina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

These changes result in ~50% improvement in the included benchmark.

PR-URL: nodejs#10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@mscdex mscdex force-pushed the stream-writable-write-perf branch from bb29b22 to 0a93728 Compare January 5, 2017 08:00
@mscdex mscdex merged commit 0a93728 into nodejs:master Jan 5, 2017
@mscdex mscdex deleted the stream-writable-write-perf branch January 5, 2017 08:03
italoacasas pushed a commit to italoacasas/node that referenced this pull request Jan 18, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: nodejs#10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this pull request Jan 19, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: nodejs#10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this pull request Jan 24, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: nodejs#10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this pull request Jan 27, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: nodejs#10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@italoacasas italoacasas mentioned this pull request Jan 29, 2017
@MylesBorins
Copy link
Contributor

@mscdex how long should these bake before LTS?

@mcollina
Copy link
Member

mcollina commented Mar 7, 2017

I'm 👍 , but I think we should assemble a release of readable-stream first that embeds those in.
(I can put some time in the next few weeks to fire a PR and post back cc @calvinmetcalf ).

@mcollina
Copy link
Member

I'm 👍 on backporting to v6.

MylesBorins pushed a commit that referenced this pull request Mar 28, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: #10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Mar 28, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: #10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Mar 29, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: #10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Apr 4, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: #10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this pull request Apr 19, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: #10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
@MylesBorins MylesBorins mentioned this pull request Apr 19, 2017
andrew749 pushed a commit to michielbaird/node that referenced this pull request Jul 19, 2017
These changes result in ~50% improvement in the included benchmark.

PR-URL: nodejs/node#10580
Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Issues and PRs related to the performance of Node.js. stream Issues and PRs related to the stream subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants