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: improve Readable.read() performance #7077

Merged
merged 1 commit into from
Jun 14, 2016

Conversation

mscdex
Copy link
Contributor

@mscdex mscdex commented May 31, 2016

Checklist
  • tests and code linting passes
  • a test and/or benchmark is included
  • the commit message follows commit guidelines
Affected core subsystem(s)
  • stream
Description of change

read() performance is improved most by switching from an array to a linked list for storing buffered data. However, other changes that also contribute include: making some hot functions inlinable, faster
read() argument checking, and misc code rearrangement to avoid unnecessary code execution.

These improvements only exist for when n is supplied to read(), otherwise performance is the same.

Note: I did change how negative n values are interpreted when in object mode. Before this PR, negative n values were interpreted as 1 in howMuchToRead(). I changed this to instead return 0 to match how negative n values are interpreted for non-object mode streams.

Results from the included benchmarks:

streams/readable-bigread.js n=1000: ./node: 306.98 ./node-master: 171.91 ....... 78.57%
streams/readable-bigunevenread.js n=1000: ./node: 213.85 ./node-master: 125.26 . 70.72%
streams/readable-boundaryread.js n=2000: ./node: 448.09 ./node-master: 298.88 .. 49.92%
streams/readable-unevenread.js n=1000: ./node: 49.565 ./node-master: 34.442 .... 43.91%
streams/readable-readall.js n=5000: ./node: 772.56 ./node-master: 766.4 ......... 0.80%

@mscdex mscdex added stream Issues and PRs related to the stream subsystem. performance Issues and PRs related to the performance of Node.js. labels May 31, 2016
@mscdex
Copy link
Contributor Author

mscdex commented May 31, 2016

/cc @nodejs/streams

@mscdex
Copy link
Contributor Author

mscdex commented May 31, 2016

@calvinmetcalf
Copy link
Contributor

should buffer list maybe live in the internals folder ?

@mscdex
Copy link
Contributor Author

mscdex commented May 31, 2016

@calvinmetcalf Debatable but I left it there since it's only used by Readable and it's better if Readable isn't referencing anything from internal/ since streams are extracted from node?

@calvinmetcalf
Copy link
Contributor

I can extract that other file easily, I'm more worried about exporting that from readable which is not on the api

@mscdex
Copy link
Contributor Author

mscdex commented May 31, 2016

@calvinmetcalf I don't understand what you mean by "is not on the api."

@calvinmetcalf
Copy link
Contributor

sorry, misspoke, I meant that I didn't like exporting that new thing from readable

@mscdex
Copy link
Contributor Author

mscdex commented May 31, 2016

@calvinmetcalf I can remove it if necessary I suppose, but then we'd end up duplicating some of it in the tests I had to modify. I thought it would be better not to have to keep two separate "implementations" in sync.

@calvinmetcalf
Copy link
Contributor

I was thinking that if that file got moved to something in internal, then you could just require that for the tests and require it for from stream_readable and don't worry about the readable-stream module, it won't be much of an inconvenience.

@mscdex mscdex force-pushed the streams-readable-faster-read branch from e90c652 to 1ae67c9 Compare May 31, 2016 23:52
@mscdex
Copy link
Contributor Author

mscdex commented May 31, 2016

@calvinmetcalf Ok, moved to internal.

@mscdex mscdex force-pushed the streams-readable-faster-read branch from 1ae67c9 to 47d6cc0 Compare May 31, 2016 23:54
}

BufferList.prototype.push = function(v) {
const entry = { data: v, next: null };
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you think it could help to create an Entry class to increase property access performance?

Copy link
Contributor Author

@mscdex mscdex Jun 1, 2016

Choose a reason for hiding this comment

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

I just tried it and it doesn't seem to make any real difference. If anything it seems to slow things down a little bit.

@mscdex
Copy link
Contributor Author

mscdex commented Jun 7, 2016

/cc @nodejs/collaborators Anyone have any comments/questions/LGTMs on this?

else
buf.copy(ret, c, 0, cpy);
// result spans more than one buffer
ret = (hasStrings
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure on the style guide on this, but the outer braces seem useless.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean the parens? shrug I always add that when "assigning" a ternary statement to a variable as it seems more readable/clear to me.

Copy link
Contributor

Choose a reason for hiding this comment

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

I noticed :) I don't really mind, just curious if there's any consensus on this. No blocker, no worries.

And sorry, yeah, parens (I, non-native English speaker, always get these mixed up... sigh)

@mscdex mscdex force-pushed the streams-readable-faster-read branch 3 times, most recently from f1922f1 to 1f2fbc2 Compare June 7, 2016 03:33

var common = require('../common');
var v8 = require('v8');
var Readable = require('stream').Readable;
Copy link
Member

Choose a reason for hiding this comment

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

const?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed.

@mscdex mscdex force-pushed the streams-readable-faster-read branch from 1f2fbc2 to 729b645 Compare June 7, 2016 03:46
@jasnell
Copy link
Member

jasnell commented Jun 7, 2016

First read through looks good but I'll have to go through in more detail tomorrow. In general tho, if @nodejs/streams is happy, then ++

@mcollina
Copy link
Member

mcollina commented Jun 7, 2016

Do we have a CITGM run?

If that passes.. LGTM. just with one catch.. Let's give this a long time before we backport.

@mscdex
Copy link
Contributor Author

mscdex commented Jun 7, 2016

@mcollina
Copy link
Member

mcollina commented Jun 7, 2016

the job was aborted, should we kick it off again?

@mscdex
Copy link
Contributor Author

mscdex commented Jun 7, 2016

@mcollina It was aborted most likely because the ppcle machine is/was still down. The lodash failure on OSX1010 looks like an npm or citgm failure since it failed on installation of the package.

Otherwise citgm is green.

@mcollina
Copy link
Member

mcollina commented Jun 7, 2016

LGTM as semver-minor.

@mscdex mscdex deleted the streams-readable-faster-read branch June 14, 2016 20:00
Fishrock123 pushed a commit that referenced this pull request Jun 27, 2016
read() performance is improved most by switching from an array to
a linked list for storing buffered data. However, other changes that
also contribute include: making some hot functions inlinable, faster
read() argument checking, and misc code rearrangement to avoid
unnecessary code execution.

PR-URL: #7077
Reviewed-By: Calvin Metcalf <calvin.metcalf@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
@Fishrock123 Fishrock123 mentioned this pull request Jun 27, 2016
Fishrock123 pushed a commit that referenced this pull request Jul 5, 2016
read() performance is improved most by switching from an array to
a linked list for storing buffered data. However, other changes that
also contribute include: making some hot functions inlinable, faster
read() argument checking, and misc code rearrangement to avoid
unnecessary code execution.

PR-URL: #7077
Reviewed-By: Calvin Metcalf <calvin.metcalf@gmail.com>
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
@Fishrock123 Fishrock123 mentioned this pull request Jul 5, 2016
@Fishrock123
Copy link
Contributor

@mcollina Why is this semver-minor?

Fishrock123 added a commit that referenced this pull request Jul 6, 2016
Notable changes:

* buffer: Added `buffer.swap64()` to compliment `swap16()` &
`swap32()`. (Zach Bjornson) #7157
* build: New `configure` options have been added for building Node.js
as a shared library. (Stefan Budeanu)
#6994
  - The options are: `--shared`, `--without-v8-platform` &
`--without-bundled-v8`.
* crypto: Root certificates have been updated. (Ben Noordhuis)
#7363
* debugger: The server address is now configurable via
`--debug=<address>:<port>`. (Ben Noordhuis)
#3316
* npm: Upgraded npm to v3.10.3 (Kat Marchán)
#7515 & (Rebecca Turner)
#7410
* readline: Added the `prompt` option to the readline constructor.
(Evan Lucas) #7125
* repl / vm: `sigint`/`ctrl+c` will now break out of infinite loops
without stopping the Node.js instance. (Anna Henningsen)
#6635
* src:
  - Added a `node::FreeEnvironment` public C++ API. (Cheng Zhao)
#3098
  - Refactored `require('constants')`, constants are now available
directly from their respective modules. (James M Snell)
#6534
* stream: Improved `readable.read()` performance by up to 70%. (Brian
White) #7077
* timers: `setImmediate()` is now up to 150% faster in some situations.
(Andras) #6436
* util: Added a `breakLength` option to `util.inspect()` to control how
objects are formatted across lines. (cjihrig)
#7499
* v8-inspector: Experimental support has been added for debugging
Node.js over the inspector protocol. (Ali Ijaz Sheikh)
#6792
  - *Note: This feature is experimental, and it could be altered or
removed.*
  - You can try this feature by running Node.js with the `--inspect`
flag.

Refs: #7441
PR-URL: #7550
@mcollina
Copy link
Member

mcollina commented Jul 6, 2016

@Fishrock123 avoiding possible breakages in a patch release. IMHO performance optimizations are semver-minor, as performance is a backward-compatible feature.

@Fishrock123
Copy link
Contributor

@mcollina So you are saying this is semver-major then? This is at obvious risk of breaking something? Do I need to back it out of 6.3.0?

How this works is, in this order:

  • Does it have a chance of breaking something? -> Major
  • Does it add something that won't work on previous versions (feature/api)? -> Minor
  • Everything else: -> Patch

@Fishrock123 Fishrock123 removed the semver-minor PRs that contain new features and should be released in the next minor version. label Jul 6, 2016
Fishrock123 added a commit that referenced this pull request Jul 6, 2016
Notable changes:

* buffer: Added `buffer.swap64()` to compliment `swap16()` &
`swap32()`. (Zach Bjornson) #7157
* build: New `configure` options have been added for building Node.js
as a shared library. (Stefan Budeanu)
#6994
  - The options are: `--shared`, `--without-v8-platform` &
`--without-bundled-v8`.
* crypto: Root certificates have been updated. (Ben Noordhuis)
#7363
* debugger: The server address is now configurable via
`--debug=<address>:<port>`. (Ben Noordhuis)
#3316
* npm: Upgraded npm to v3.10.3 (Kat Marchán)
#7515 & (Rebecca Turner)
#7410
* readline: Added the `prompt` option to the readline constructor.
(Evan Lucas) #7125
* repl / vm: `sigint`/`ctrl+c` will now break out of infinite loops
without stopping the Node.js instance. (Anna Henningsen)
#6635
* src:
  - Added a `node::FreeEnvironment` public C++ API. (Cheng Zhao)
#3098
  - Refactored `require('constants')`, constants are now available
directly from their respective modules. (James M Snell)
#6534
* stream: Improved `readable.read()` performance by up to 70%. (Brian
White) #7077
* timers: `setImmediate()` is now up to 150% faster in some situations.
(Andras) #6436
* util: Added a `breakLength` option to `util.inspect()` to control how
objects are formatted across lines. (cjihrig)
#7499
* v8-inspector: Experimental support has been added for debugging
Node.js over the inspector protocol. (Ali Ijaz Sheikh)
#6792
  - *Note: This feature is experimental, and it could be altered or
removed.*
  - You can try this feature by running Node.js with the `--inspect`
flag.

Refs: #7441
PR-URL: #7550
@mcollina
Copy link
Member

mcollina commented Jul 6, 2016

@Fishrock123

So you are saying this is semver-major then? This is at obvious risk of breaking something? Do I need to back it out of 6.3.0?

no and no.
We are touching streams, and based on latest history, there is a slight higher risk of breaking something compared to other part of core. Because of this, I would put them into semver-minor anything that is not strictly a bug fix.

This is a performance optimization, e.g. a new feature for me.

@Fishrock123
Copy link
Contributor

So... don't land on LTS is what I'm getting out of this. :)

@mcollina
Copy link
Member

mcollina commented Jul 6, 2016

@Fishrock123 I think this one will be fine on LTS, but I would recommend porting it after some months, not weeks. Something like "land when the new LTS is out".

Fishrock123 added a commit that referenced this pull request Jul 6, 2016
Notable changes:

* buffer: Added `buffer.swap64()` to compliment `swap16()` &
`swap32()`. (Zach Bjornson) #7157
* build: New `configure` options have been added for building Node.js
as a shared library. (Stefan Budeanu)
#6994
  - The options are: `--shared`, `--without-v8-platform` &
`--without-bundled-v8`.
* crypto: Root certificates have been updated. (Ben Noordhuis)
#7363
* debugger: The server address is now configurable via
`--debug=<address>:<port>`. (Ben Noordhuis)
#3316
* npm: Upgraded npm to v3.10.3 (Kat Marchán)
#7515 & (Rebecca Turner)
#7410
* readline: Added the `prompt` option to the readline constructor.
(Evan Lucas) #7125
* repl / vm: `sigint`/`ctrl+c` will now break out of infinite loops
without stopping the Node.js instance. (Anna Henningsen)
#6635
* src:
  - Added a `node::FreeEnvironment` public C++ API. (Cheng Zhao)
#3098
  - Refactored `require('constants')`, constants are now available
directly from their respective modules. (James M Snell)
#6534
* stream: Improved `readable.read()` performance by up to 70%. (Brian
White) #7077
* timers: `setImmediate()` is now up to 150% faster in some situations.
(Andras) #6436
* util: Added a `breakLength` option to `util.inspect()` to control how
objects are formatted across lines. (cjihrig)
#7499
* v8-inspector: Experimental support has been added for debugging
Node.js over the inspector protocol. (Ali Ijaz Sheikh)
#6792
  - *Note: This feature is experimental, and it could be altered or
removed.*
  - You can try this feature by running Node.js with the `--inspect`
flag.

Refs: #7441
PR-URL: #7550
lukesampson pushed a commit to ScoopInstaller/Scoop that referenced this pull request Jul 7, 2016
### Notable changes

* **buffer**: Added `buffer.swap64()` to compliment `swap16()` & `swap32()`. (Zach Bjornson) [#7157](nodejs/node#7157)
* **build**: New `configure` options have been added for building Node.js as a shared library. (Stefan Budeanu) [#6994](nodejs/node#6994)
  - The options are: `--shared`, `--without-v8-platform` & `--without-bundled-v8`.
* **crypto**: Root certificates have been updated. (Ben Noordhuis) [#7363](nodejs/node#7363)
* **debugger**: The server address is now configurable via `--debug=<address>:<port>`. (Ben Noordhuis) [#3316](nodejs/node#3316)
* **npm**: Upgraded npm to v3.10.3 (Kat Marchán) [#7515](nodejs/node#7515) & (Rebecca Turner) [#7410](nodejs/node#7410)
* **readline**: Added the `prompt` option to the readline constructor. (Evan Lucas) [#7125](nodejs/node#7125)
* **repl / vm**: `sigint`/`ctrl+c` will now break out of infinite loops without stopping the Node.js instance. (Anna Henningsen) [#6635](nodejs/node#6635)
* **src**:
  - Added a `node::FreeEnvironment` public C++ API. (Cheng Zhao) [#3098](nodejs/node#3098)
  - Refactored `require('constants')`, constants are now available directly from their respective modules. (James M Snell) [#6534](nodejs/node#6534)
* **stream**: Improved `readable.read()` performance by up to 70%. (Brian White) [#7077](nodejs/node#7077)
* **timers**: `setImmediate()` is now up to 150% faster in some situations. (Andras) [#6436](nodejs/node#6436)
* **util**: Added a `breakLength` option to `util.inspect()` to control how objects are formatted across lines. (cjihrig) [#7499](nodejs/node#7499)
* **v8-inspector**: Experimental support has been added for debugging Node.js over the inspector protocol. (Ali Ijaz Sheikh) [#6792](nodejs/node#6792)
  - **Note: This feature is _experimental_, and it could be altered or removed.**
  - You can try this feature by running Node.js with the `--inspect` flag.
@mafintosh
Copy link
Member

mafintosh commented Jul 8, 2016

@Fishrock123 @mcollina just re your above conversation, this ended up breaking duplexify (https://www.npmjs.com/package/duplexify) because it expected the _buffer to be an array

@mcollina
Copy link
Member

mcollina commented Jul 8, 2016

I feared something like that would have happened.

I think we should make a final decision either to semver _readableState and _writableState API or not.
Formally, they are not part of the public API, and these are causing all sort of issues on streams modules.

I'm happy we got this early (before a backport to readable-stream).

@ronkorving
Copy link
Contributor

They're clearly private. If people want to manipulate them, we can expose public API for that. The duplexify package authors could've pushed/asked for that, and still can imho.

@mafintosh
Copy link
Member

@mcollina could you open a separate issue for the semver discussion so we don't take over this one? for the record i think reading some of the properties in _readableState is essential for a bunch of use-cases. like figuring out if a stream is in objectMode, figuring out if it ended but still has data in its read buffer etc.

@mcollina
Copy link
Member

mcollina commented Jul 9, 2016

@mafintosh done ^

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