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

Possibly deoptimizing use of arguments in libs #10323

Closed
vsemozhetbyt opened this issue Dec 18, 2016 · 14 comments
Closed

Possibly deoptimizing use of arguments in libs #10323

vsemozhetbyt opened this issue Dec 18, 2016 · 14 comments
Labels
good first issue Issues that are suitable for first-time contributors. lib / src Issues and PRs related to general changes in the lib or src directory. performance Issues and PRs related to the performance of Node.js. v8 engine Issues and PRs related to the V8 dependency.

Comments

@vsemozhetbyt
Copy link
Contributor

vsemozhetbyt commented Dec 18, 2016

I've started to read the v8-bailout-reasons, in particular the "Bad value context for arguments value" part. @vhf refers to this part of more detailed research, where we can find some safety rules about arguments. So I've tried to scan Node.js libs for possible violations of these rules. Here are the suspicious fragments I've found.

  1. Leaking arguments:

domain.js: function Domain.prototype.intercept leaks here. (Possibly is not worth the efforts; see this comment)
domain.js: function Domain.prototype.bind leaks here. (Possibly is not worth the efforts; see this comment)
internal/process.js: function setupConfig leaks here. (Possibly is not worth the efforts; see this comment)
internal/util.js: function exports._deprecate leaks here. (It does not cause deopt; see this PR)

  1. Using arguments[index] with a possibility of index to be out of the arguments bounds.

This is more difficult case to check, but I've inferred possible arguments.length variability from docs and code comments. Maybe I'm wrong for some or all cases. I will refer to a first possible violation in a function, the function can contain more.

child_process.js: function normalizeExecArgs uses possible out of bounds index(es) from here. (Fixed)
child_process.js: function exports.execFile uses possible out of bounds index(es) from here. (Fixed)
child_process.js: function normalizeSpawnArguments uses possible out of bounds index(es) from here. (Fixed)

dgram.js: function Socket.prototype.bind uses possible out of bounds index(es) from here. (Fixed)

events.js: function EventEmitter.prototype.emit uses possible out of bounds index(es) from here.
There is a sign of awareness in the code, but maybe ES6 makes it possible to resolve this difficulty in the EventEmitter.prototype.emit itself.
(Fixed)

fs.js: all the functions with maybeCallback or makeCallback calls can use possible out of bounds index(es). (they are not called without parameters; see this PR)

util.js: function inspect uses possible out of bounds index(es) from here. (Fixed)

_debugger.js: function Interface.prototype.scripts uses possible out of bounds index(es) from here. (Possibly is not worth the efforts; see this comment)
_debugger.js: function Interface.prototype.watchers uses possible out of bounds index(es) from here. (Possibly is not worth the efforts; see this comment)

I am not sure about any of these cases: if they are real violations, if these violations have any real impact on the performance and so on. And unfortunately, I have not sufficient knowledge of all the codebase system to propose any changes here. So take this as a start point memo for contributors who consider it to be worth any attention and close it if this is some needless overagitating.

P.S. Part 2.

@Fishrock123 Fishrock123 added lib / src Issues and PRs related to general changes in the lib or src directory. v8 engine Issues and PRs related to the V8 dependency. labels Dec 18, 2016
@mscdex mscdex added the performance Issues and PRs related to the performance of Node.js. label Dec 18, 2016
@not-an-aardvark
Copy link
Contributor

I'm not familiar enough with V8 to know whether these uses of arguments cause a noticeable performance drop, but if we conclude that they are worth avoiding, it seems like it should be possible to make a linter rule for them.

@vsemozhetbyt
Copy link
Contributor Author

Eslint has a rather radical rule for all arguments usecases. However, the mentioned researches propose less drastic workarounds. For example, index checking, arguments copying and .apply() mediation are already used in many lib places.

@vhf
Copy link
Contributor

vhf commented Dec 19, 2016

Interesting, nice findings @vsemozhetbyt .

Fixing the possible out of bound access to the arguments object might be worth it. I glanced at the other things you mentioned and it might be either complicated to fix or not worth the effort or both.

I'm not familiar with node internals so I might be wrong, but here is an example: setupConfig leaks arguments. It's a tiny utility method and I'm pretty sure its use is extremely seldom. I'm pretty sure this function not being optimized cannot have any sort of significant performance impact.

What might be slightly more interesting in terms of performances, but might break some people's code, would be to set process.icu_data_dir to null instead of deleteing it. This way the process object will stay fast (because of delete it currently becomes a dictionary mode object and we can assume this slow object will be reused in people's code).

@vsemozhetbyt
Copy link
Contributor Author

vsemozhetbyt commented Dec 19, 2016

@vhf Thank you for the answer.

As I have said, I'm also not aware of all the system. For example, the mentioned setupConfig() function is exported from the internal lib and it needs to be considered in addition how many times it can be called in all the lib system and how many times it can be triggered by various user code. (By the way, it seems you have used by accident the link to the documented property of another lib of the same name instead of the function of the internal lib).

And thank you for the second comment. It will need more checks for other similar cases. Is this feature connected with v8 hidden classes you've described recently? I've also read about it in another article (chapter "Objects")

@bnoordhuis
Copy link
Member

I suspect the only functions where it could make a material difference are EventEmitter#emit() and util.inspect(), the others are unlikely to register under normal conditions.

Fixing the other functions might still be worthwhile from a code hygiene perspective, but not if it significantly affects complexity or readability.

@Fishrock123
Copy link
Member

Fishrock123 commented Dec 19, 2016

(If you'd like to help fix this look here.)

My assessment:

  • Domain.prototype.intercept - ¯\_(ツ)_/¯
  • Domain.prototype.bind - ¯\_(ツ)_/¯
  • setupConfig (... really v8BreakIterator) - ¯\_(ツ)_/¯
  • exports._deprecate - ⚠️ We should probably check this one.
  • child_process.js > normalizeExecArgs - ⚠️ Should probably fix that.
  • child_process.js > exports.execFile - Are you sure this can happen out of bounds? Probably fix.
  • child_process.js > normalizeSpawnArgs - ⚠️ Should probably fix that.
  • dgram.js > Socket.prototype.bind - ⚠️ Should bail if no arguments before this statement.
  • events.js > EventEmitter.prototype.emit - ❗️Almost certainly important. Check for no args passed.
  • fs I didn't check, but sounds ⚠️.
  • util.js > inspect. - ❗️Almost certainly important. Check arg counts in these.
  • _debugger.js - ¯\_(ツ)_/¯

@vsemozhetbyt
Copy link
Contributor Author

vsemozhetbyt commented Dec 19, 2016

@Fishrock123

child_process.js > exports.execFile - Are you sure this can happen out of bounds? Probably fix.

Well, suppose we have only the first argument (file) set. So arguments.length is 1 (and only arguments[0] is set). So all these if's are skipped and pos remains 1 and callback remains undefined. So arguments[pos] here is arguments[1], i.e. out of bounds. Maybe I miss something.

@Fishrock123 Fishrock123 added the good first issue Issues that are suitable for first-time contributors. label Dec 20, 2016
@mgol
Copy link
Contributor

mgol commented Dec 23, 2016

Do rest parameters have the same deopt problems as the arguments object? Are there any reasons why Node still uses arguments when it can use the full ES6 power?

@CapacitorSet
Copy link

@mgol, rest parameters are a known Crankshaft bailout (deoptimization) reason at this time.

@bmeurer
Copy link
Member

bmeurer commented Jan 1, 2017

@mgol Rest parameters force optimization via TurboFan, which should be fine in many cases, esp. with V8 5.6 and later (not yet available for Node.js tho).

@bmeurer
Copy link
Member

bmeurer commented Jan 1, 2017

@Fishrock123 EventEmitter.prototype.emit is pretty serious, esp. since with newer versions of V8, Crankshaft will disable optimization for the whole function (due a correctness fix wrt. Crankshaft's handling of arguments). See crbug.com/v8/3829 for more information on this particular magic.

@bmeurer
Copy link
Member

bmeurer commented Jan 2, 2017

Actually, what I said wasn't correct. It's a bug in Crankshaft that we disable optimization (in 5.6 and later) for emit; fix in-flight here.

vhf added a commit to vhf/node that referenced this issue Jan 2, 2017
This commit makes sure EventEmitter.emit() doesn't get deoptimized by
V8. The deopt happens when accessing out of bound indexes of the
`arguments` object.

This issue has been raised here: nodejs#10323 and this specific case might
become a more serious performance issue in upcoming V8 releases.
Fishrock123 added a commit to Fishrock123/node that referenced this issue Jan 3, 2017
not-an-aardvark added a commit to not-an-aardvark/node that referenced this issue Jan 5, 2017
This updates util.inspect() to avoid accessing out-of-range indices of
the `arguments` object, which is known to cause optimization bailout.

Based on an average of 10 runs of the benchmark in
`benchmark/util/inspect.js`, this change improves the performance of
`util.inspect` by about 10%.

Relates to nodejs#10323

PR-URL: nodejs#10569
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
Reviewed-By: Jackson Tian <shyvo1987@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
jasnell pushed a commit that referenced this issue Jan 5, 2017
This commit makes sure EventEmitter.emit() doesn't get deoptimized by
V8. The deopt happens when accessing out of bound indexes of the
`arguments` object.

This issue has been raised here: #10323 and this specific case might
become a more serious performance issue in upcoming V8 releases.

PR-URL: #10568
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 18, 2017
This updates util.inspect() to avoid accessing out-of-range indices of
the `arguments` object, which is known to cause optimization bailout.

Based on an average of 10 runs of the benchmark in
`benchmark/util/inspect.js`, this change improves the performance of
`util.inspect` by about 10%.

Relates to nodejs#10323

PR-URL: nodejs#10569
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
Reviewed-By: Jackson Tian <shyvo1987@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 18, 2017
This commit makes sure EventEmitter.emit() doesn't get deoptimized by
V8. The deopt happens when accessing out of bound indexes of the
`arguments` object.

This issue has been raised here: nodejs#10323 and this specific case might
become a more serious performance issue in upcoming V8 releases.

PR-URL: nodejs#10568
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 19, 2017
This updates util.inspect() to avoid accessing out-of-range indices of
the `arguments` object, which is known to cause optimization bailout.

Based on an average of 10 runs of the benchmark in
`benchmark/util/inspect.js`, this change improves the performance of
`util.inspect` by about 10%.

Relates to nodejs#10323

PR-URL: nodejs#10569
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
Reviewed-By: Jackson Tian <shyvo1987@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 19, 2017
This commit makes sure EventEmitter.emit() doesn't get deoptimized by
V8. The deopt happens when accessing out of bound indexes of the
`arguments` object.

This issue has been raised here: nodejs#10323 and this specific case might
become a more serious performance issue in upcoming V8 releases.

PR-URL: nodejs#10568
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 24, 2017
This updates util.inspect() to avoid accessing out-of-range indices of
the `arguments` object, which is known to cause optimization bailout.

Based on an average of 10 runs of the benchmark in
`benchmark/util/inspect.js`, this change improves the performance of
`util.inspect` by about 10%.

Relates to nodejs#10323

PR-URL: nodejs#10569
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
Reviewed-By: Jackson Tian <shyvo1987@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 24, 2017
This commit makes sure EventEmitter.emit() doesn't get deoptimized by
V8. The deopt happens when accessing out of bound indexes of the
`arguments` object.

This issue has been raised here: nodejs#10323 and this specific case might
become a more serious performance issue in upcoming V8 releases.

PR-URL: nodejs#10568
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 27, 2017
This updates util.inspect() to avoid accessing out-of-range indices of
the `arguments` object, which is known to cause optimization bailout.

Based on an average of 10 runs of the benchmark in
`benchmark/util/inspect.js`, this change improves the performance of
`util.inspect` by about 10%.

Relates to nodejs#10323

PR-URL: nodejs#10569
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
Reviewed-By: Jackson Tian <shyvo1987@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Jan 27, 2017
This commit makes sure EventEmitter.emit() doesn't get deoptimized by
V8. The deopt happens when accessing out of bound indexes of the
`arguments` object.

This issue has been raised here: nodejs#10323 and this specific case might
become a more serious performance issue in upcoming V8 releases.

PR-URL: nodejs#10568
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
@vsemozhetbyt
Copy link
Contributor Author

vsemozhetbyt commented Feb 2, 2017

The initial comment is updated considering this opinion and all the committed fixes.

tjhosford added a commit to tjhosford/node that referenced this issue Feb 2, 2017
This updates fs to prevent accessing out-of-range indices on the arguments object, which is known to cause V8 optimization bailout.

Related to issues discussed here: nodejs#10323
cjihrig pushed a commit that referenced this issue Feb 16, 2017
This commit adds a guard against an out of bounds access of
arguments, and replaces another use of arguments with a named
function parameter.

Refs: #10323
PR-URL: #11242
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit to italoacasas/node that referenced this issue Feb 20, 2017
This commit adds a guard against an out of bounds access of
arguments, and replaces another use of arguments with a named
function parameter.

Refs: nodejs#10323
PR-URL: nodejs#11242
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
italoacasas pushed a commit that referenced this issue Feb 22, 2017
This commit adds a guard against an out of bounds access of
arguments, and replaces another use of arguments with a named
function parameter.

Refs: #10323
PR-URL: #11242
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
cjihrig pushed a commit that referenced this issue Mar 1, 2017
Removed or fixed use of arguments in execFile(),
normalizeExecArgs(), and normalizeSpawnArguments().

Refs: #10323
Refs: https://bugs.chromium.org/p/v8/issues/detail?id=6010
PR-URL: #11535
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
@vsemozhetbyt
Copy link
Contributor Author

It seems all the mentioned cases have been fixed or reasonably rejected, so this can be closed.

jasnell pushed a commit that referenced this issue Mar 9, 2017
This commit adds a guard against an out of bounds access of
arguments, and replaces another use of arguments with a named
function parameter.

Refs: #10323
PR-URL: #11242
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this issue Mar 9, 2017
This commit adds a guard against an out of bounds access of
arguments, and replaces another use of arguments with a named
function parameter.

Refs: #10323
PR-URL: #11242
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
MylesBorins pushed a commit that referenced this issue Mar 9, 2017
This commit adds a guard against an out of bounds access of
arguments, and replaces another use of arguments with a named
function parameter.

Refs: #10323
MylesBorins pushed a commit that referenced this issue Mar 9, 2017
This commit adds a guard against an out of bounds access of
arguments, and replaces another use of arguments with a named
function parameter.

Refs: #10323
jasnell pushed a commit that referenced this issue Mar 16, 2017
Removed or fixed use of arguments in execFile(),
normalizeExecArgs(), and normalizeSpawnArguments().

Refs: #10323
Refs: https://bugs.chromium.org/p/v8/issues/detail?id=6010

Backport-Of: #11535
PR-URL: #11748
Reviewed-By: James M Snell <jasnell@gmail.com>
gibfahn pushed a commit that referenced this issue Jun 20, 2017
Remove use of arguments in
normalizeExecArgs() and normalizeSpawnArguments().

Refs: #10323
PR-URL: #11535
Backport-PR-URL: #13752
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
MylesBorins pushed a commit that referenced this issue Jul 11, 2017
Remove use of arguments in
normalizeExecArgs() and normalizeSpawnArguments().

Refs: #10323
PR-URL: #11535
Backport-PR-URL: #13752
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Issues that are suitable for first-time contributors. lib / src Issues and PRs related to general changes in the lib or src directory. performance Issues and PRs related to the performance of Node.js. v8 engine Issues and PRs related to the V8 dependency.
Projects
None yet
Development

No branches or pull requests

9 participants