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

lib,src: improve writev() performance for Buffers #13187

Merged
merged 1 commit into from
May 26, 2017

Conversation

mscdex
Copy link
Contributor

@mscdex mscdex commented May 24, 2017

It's pretty common to write only Buffers to a socket, so we can optimize for that case in a couple of ways whenever we know we have all Buffers queued up for writev():

  • Reuse the existing array of chunks
  • Try to write the Buffers (or at least as much as possible) immediately without having to create a WriteWrap instance first. This optimization is one that is already being done when write()ing a single Buffer (or a single string for that matter) to a socket.

Benchmark results:

                                                improvement confidence      p.value
 net/net-c2s-cork.js dur=5 type="buf" len=1024     32.36 %        *** 3.042522e-23
 net/net-c2s-cork.js dur=5 type="buf" len=128      27.74 %        *** 5.759989e-59
 net/net-c2s-cork.js dur=5 type="buf" len=16       30.72 %        *** 1.594866e-57
 net/net-c2s-cork.js dur=5 type="buf" len=32       31.18 %        *** 8.568983e-62
 net/net-c2s-cork.js dur=5 type="buf" len=4        24.99 %        *** 2.220787e-64
 net/net-c2s-cork.js dur=5 type="buf" len=512      29.04 %        *** 3.058048e-32
 net/net-c2s-cork.js dur=5 type="buf" len=64       29.34 %        *** 5.167685e-50
 net/net-c2s-cork.js dur=5 type="buf" len=8        11.35 %        *** 4.258937e-43

CI: https://ci.nodejs.org/job/node-test-pull-request/8271/
CI for FIPS (since it failed initially): https://ci.nodejs.org/job/node-test-commit-linux-fips/8624/

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

@mscdex mscdex added c++ Issues and PRs that require attention from people who are familiar with C++. net Issues and PRs related to the net subsystem. performance Issues and PRs related to the performance of Node.js. stream Issues and PRs related to the stream subsystem. labels May 24, 2017
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. net Issues and PRs related to the net subsystem. stream Issues and PRs related to the stream subsystem. labels May 24, 2017
while (entry) {
buffer[count] = entry;
if (!entry.isBuf)
Copy link
Member

Choose a reason for hiding this comment

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

I'd probably do allBuffers = allBuffers && entry.isBuf

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 tried something similar (continuing the rest of the loop without any conditional) and did not see any difference in performance.

@@ -100,94 +100,118 @@ int StreamBase::Writev(const FunctionCallbackInfo<Value>& args) {

Local<Object> req_wrap_obj = args[0].As<Object>();
Local<Array> chunks = args[1].As<Array>();
bool all_buffers = args[2]->BooleanValue();
Copy link
Member

Choose a reason for hiding this comment

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

Can you use the overload that returns a Maybe<bool>, or alternatively use IsTrue() instead?

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.

if (all_buffers)
count = chunks->Length();
else
count = chunks->Length() >> 1;
Copy link
Member

Choose a reason for hiding this comment

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

nit: I’d really just write / 2, it’s clearer and the resulting code will be the same

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's copied from before these changes. It's also similar to what was already being used in js.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, but in JS that has a different meaning. ¯\_(ツ)_/¯ If you want to keep it it’s fine.

wrap = GetAsyncWrap();
// NOTE: All tests show that GetAsyncWrap() never returns nullptr here. If it
// can then replace the CHECK_NE() with if (wrap != nullptr).
CHECK_NE(wrap, nullptr);
Copy link
Member

Choose a reason for hiding this comment

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

Heads up, this is going to conflict with #13174 which removes the comment (because GetAsyncWrap() does actually never return a null pointer), so feel free to remove it here as well

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'll just wait until that lands first and I'll rebase.

Copy link
Member

Choose a reason for hiding this comment

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

@mscdex Landed, you can rebase now

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done.

size_t offset = 0;
for (size_t i = 0; i < count; i++) {
Local<Value> chunk = chunks->Get(i * 2);
if (Buffer::HasInstance(chunk))
Copy link
Member

Choose a reason for hiding this comment

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

nit: chunk->IsUint8Array()

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was also copied from before these changes. I think changing it to check for a more general type is outside the scope of this PR.

Copy link
Member

Choose a reason for hiding this comment

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

I think changing it to check for a more general type is outside the scope of this PR.

It’s literally the same thing, just inlined. :)

Copy link
Contributor Author

@mscdex mscdex May 24, 2017

Choose a reason for hiding this comment

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

Actually HasInstance() in master and v8.x use IsArrayBufferView() while v7.x and v6.x use IsUint8Array(). Ideally this could be backported more easily if we kept HasInstance()?

Copy link
Member

Choose a reason for hiding this comment

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

Oh, right, we made that switch. Idc, you can also keep Buffer::HasInstance.

@mscdex mscdex force-pushed the net-writev-perf branch 2 times, most recently from c5814a0 to fd84f30 Compare May 25, 2017 18:33
PR-URL: nodejs#13187
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
@mscdex mscdex force-pushed the net-writev-perf branch from fd84f30 to 01a1022 Compare May 26, 2017 08:30
@mscdex mscdex merged commit 01a1022 into nodejs:master May 26, 2017
@mscdex mscdex deleted the net-writev-perf branch May 26, 2017 08:33
jasnell pushed a commit that referenced this pull request May 28, 2017
PR-URL: #13187
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Anna Henningsen <anna@addaleax.net>
@jasnell jasnell mentioned this pull request May 28, 2017
@gibfahn gibfahn mentioned this pull request Jun 15, 2017
3 tasks
@MylesBorins MylesBorins added baking-for-lts PRs that need to wait before landing in a LTS release. lts-watch-v6.x labels Jul 17, 2017
@MylesBorins
Copy link
Contributor

Should we consider this for LTS? if so it needs to bake for a bit. Please change labels as appropriate

@MylesBorins MylesBorins removed the baking-for-lts PRs that need to wait before landing in a LTS release. label Aug 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c++ Issues and PRs that require attention from people who are familiar with C++. net Issues and PRs related to the net subsystem. 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.

5 participants