-
Notifications
You must be signed in to change notification settings - Fork 30.1k
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: add throws option to fs.f/l/statSync #33716
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is another way to improve performance in case you expect many errors that you are not interested in: using Error.stackTraceLimit
. This may be set to zero before the operation and should be reset afterwards. It will not be the same gain as adding the option here (likely around half the performance gain) but it's possible to apply this pattern everywhere. The reason why this is a significant improvement is the overhead of computing the stack frames.
@amcasey I guess you already use the |
@BridgeAR Yes, discovering that was a huge win - thanks! |
I tried this on the same data set I used to validate my change and saw a change that was close enough to zero that I'd want to do a lot more iterations before declaring one way or the other. (For simplicity, I just set Edit: While sanity checking that I had, in fact, run against the correct version of the code, I discovered that it subsequently sets the Edit 2: The comment is wrong - the code setting |
@amcasey it would depend if any other code resets the stack trace limit to a different value. I just ran a quick benchmark in the REPL: > console.time(); var limit = Error.stackTraceLimit; Error.stackTraceLimit = 0; for (let i = 0; i < 1e6; i++) try { throw new Error() } catch {}; Error.stackTraceLimit = limit; console.timeEnd()
default: 2.380s
> console.time(); for (let i = 0; i < 1e6; i++) try { throw new Error() } catch {}; console.timeEnd()
default: 7.260s |
@BridgeAR We only have one explicit access to the property and it's not hit in the case I tested. Is it possible that some other API we're calling reset it? Also, I didn't mean to imply that it doesn't work in any scenario, just that it doesn't obviously work in ours. |
That is absolutely possible. You could check what is set while exiting (e.g., |
ping @nodejs/fs @BridgeAR |
Beyond just the operational overhead cost, debugging errors can become more difficult when code is throwing exceptions during normal operations. If you debug an application and set it to break on all exceptions, it will break every time some piece of code tries to check for file existence. For something like the TypeScript compiler, this means hundreds or thousands of expected exceptions as the compiler probes for files on disk (file not found is a very much expected result). While in some cases you can work around this by setting a breakpoint after the file probing is complete and then enabling uncaught exceptions, this strategy doesn't work when the exception you are trying to break on happens in the middle of a large amount of file probing, or when the application you are debugging just naturally does a lot of file probing. |
ping @BridgeAR @nodejs/fs |
Let's start with the bits I agree with. :-) Yes, However, rather than adding a |
Looking at the docs, that doesn't appear to address the problem of throwing being expensive, since that function still throws in all failure scenarios:
|
|
ping @BridgeAR @bnoordhuis @nodejs/fs |
@joyeecheung Can you please point me at the failure in
The other stack traces in the file look like Jenkins infrastructure issues (possibly as a result of something upstream I haven't identified). |
@joyeecheung The benchmark run shows the exception that would occur if run in a version of node without this change:
Is there some way to confirm that it actually did have this change? Locally, I see that failure with "stock" node and this output with my built version:
|
Notable changes: dns: * (SEMVER-MINOR) add a cancel() method to the promise Resolver (Szymon Marczak) #33099 events: * (SEMVER-MINOR) add max listener warning for EventTarget (James M Snell) #36001 http: * (SEMVER-MINOR) add support for abortsignal to http.request (Benjamin Gruenbaum) #36048 http2: * (SEMVER-MINOR) allow setting the local window size of a session (Yongsheng Zhang) #35978 lib: * (SEMVER-MINOR) add throws option to fs.f/l/statSync (Andrew Casey) #33716 path: * (SEMVER-MINOR) add `path/posix` and `path/win32` alias modules (ExE Boss) #34962 readline: * (SEMVER-MINOR) add getPrompt to get the current prompt (Mattias Runge-Broberg) #33675 src: * (SEMVER-MINOR) add loop idle time in diagnostic report (Gireesh Punathil) #35940 util: * (SEMVER-MINOR) add `util/types` alias module (ExE Boss) #34055 PR-URL: TODO
Future versions of node will be able to return undefined, rather than allocating and throwing an exception, when a file is not found. See nodejs/node#33716
Notable changes: dns: * (SEMVER-MINOR) add a cancel() method to the promise Resolver (Szymon Marczak) #33099 events: * (SEMVER-MINOR) add max listener warning for EventTarget (James M Snell) #36001 http: * (SEMVER-MINOR) add support for abortsignal to http.request (Benjamin Gruenbaum) #36048 http2: * (SEMVER-MINOR) allow setting the local window size of a session (Yongsheng Zhang) #35978 lib: * (SEMVER-MINOR) add throws option to fs.f/l/statSync (Andrew Casey) #33716 path: * (SEMVER-MINOR) add `path/posix` and `path/win32` alias modules (ExE Boss) #34962 readline: * (SEMVER-MINOR) add getPrompt to get the current prompt (Mattias Runge-Broberg) #33675 src: * (SEMVER-MINOR) add loop idle time in diagnostic report (Gireesh Punathil) #35940 util: * (SEMVER-MINOR) add `util/types` alias module (ExE Boss) #34055 PR-URL: TODO
Notable changes: dns: * (SEMVER-MINOR) add a cancel() method to the promise Resolver (Szymon Marczak) #33099 events: * (SEMVER-MINOR) add max listener warning for EventTarget (James M Snell) #36001 http: * (SEMVER-MINOR) add support for abortsignal to http.request (Benjamin Gruenbaum) #36048 http2: * (SEMVER-MINOR) allow setting the local window size of a session (Yongsheng Zhang) #35978 lib: * (SEMVER-MINOR) add throws option to fs.f/l/statSync (Andrew Casey) #33716 path: * (SEMVER-MINOR) add `path/posix` and `path/win32` alias modules (ExE Boss) #34962 readline: * (SEMVER-MINOR) add getPrompt to get the current prompt (Mattias Runge-Broberg) #33675 src: * (SEMVER-MINOR) add loop idle time in diagnostic report (Gireesh Punathil) #35940 util: * (SEMVER-MINOR) add `util/types` alias module (ExE Boss) #34055 PR-URL: #36232
Notable changes: dns: * (SEMVER-MINOR) add a cancel() method to the promise Resolver (Szymon Marczak) #33099 events: * (SEMVER-MINOR) add max listener warning for EventTarget (James M Snell) #36001 http: * (SEMVER-MINOR) add support for abortsignal to http.request (Benjamin Gruenbaum) #36048 http2: * (SEMVER-MINOR) allow setting the local window size of a session (Yongsheng Zhang) #35978 lib: * (SEMVER-MINOR) add throws option to fs.f/l/statSync (Andrew Casey) #33716 path: * (SEMVER-MINOR) add `path/posix` and `path/win32` alias modules (ExE Boss) #34962 readline: * (SEMVER-MINOR) add getPrompt to get the current prompt (Mattias Runge-Broberg) #33675 src: * (SEMVER-MINOR) add loop idle time in diagnostic report (Gireesh Punathil) #35940 util: * (SEMVER-MINOR) add `util/types` alias module (ExE Boss) #34055 PR-URL: #36232
* `throwIfNoEntry` {boolean} Whether an exception will be thrown | ||
if no file system entry exists, rather than returning `undefined`. | ||
**Default:** `true`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could the history be updated to reflect when this was added please?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@merceyz Sorry, I'm not familiar with that process. Is there a changelog or something that needs to be updated? Can you point me at a sample or a doc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume the history a few lines above this is where it's added but I'm also not familiar with it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh, I didn't see that. I guess I'm hoping it's generated, because I don't know how I'd know what version to use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It isn't i'm afraid cc @MylesBorins
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some of the history items above (e.g. 7b5b8ef) make it look like the changelog was updated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's in the changelog yes but not the history https://nodejs.org/docs/latest-v15.x/api/fs.html#fs_fs_statsync_path_options
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. So you'd like a new PR with Added in: v15.3.0
in each of the new sections?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes please :)
For consumers that aren't interested in *why* a `statSync` call failed, allocating and throwing an exception is an unnecessary expense. This PR adds an option that will cause it to return `undefined` in such cases instead. As a motivating example, the JavaScript & TypeScript language service shared between Visual Studio and Visual Studio Code is stuck with synchronous file IO for architectural and backward-compatibility reasons. It frequently needs to speculatively check for the existence of files and directories that may not exist (and cares about file vs directory, so `existsSync` is insufficient), but ignores file system entries it can't access, regardless of the reason. Benchmarking the language service is difficult because it's so hard to get good coverage of both code bases and user behaviors, but, as a representative metric, we measured batch compilation of a few hundred popular projects (by star count) from GitHub and found that, on average, we saved about 1-2% of total compilation time. We speculate that the savings could be even more significant in interactive (language service or watch mode) scenarios, where the same (non-existent) files need to be polled over and over again. It's not a huge improvement, but it's a very small change and it will affect a lot of users (and CI runs). For reference, our measurements were against `v12.x` (3637a06 at the time) on an Ubuntu Server desktop with an SSD. PR-URL: nodejs#33716 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
For consumers that aren't interested in *why* a `statSync` call failed, allocating and throwing an exception is an unnecessary expense. This PR adds an option that will cause it to return `undefined` in such cases instead. As a motivating example, the JavaScript & TypeScript language service shared between Visual Studio and Visual Studio Code is stuck with synchronous file IO for architectural and backward-compatibility reasons. It frequently needs to speculatively check for the existence of files and directories that may not exist (and cares about file vs directory, so `existsSync` is insufficient), but ignores file system entries it can't access, regardless of the reason. Benchmarking the language service is difficult because it's so hard to get good coverage of both code bases and user behaviors, but, as a representative metric, we measured batch compilation of a few hundred popular projects (by star count) from GitHub and found that, on average, we saved about 1-2% of total compilation time. We speculate that the savings could be even more significant in interactive (language service or watch mode) scenarios, where the same (non-existent) files need to be polled over and over again. It's not a huge improvement, but it's a very small change and it will affect a lot of users (and CI runs). For reference, our measurements were against `v12.x` (3637a06 at the time) on an Ubuntu Server desktop with an SSD. PR-URL: #33716 Backport-PR-URL: #36921 Reviewed-By: Denys Otrishko <shishugi@gmail.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Future versions of node will be able to return undefined, rather than allocating and throwing an exception, when a file is not found. See nodejs/node#33716
Future versions of node will be able to return undefined, rather than allocating and throwing an exception, when a file is not found. See nodejs/node#33716
Future versions of node will be able to return undefined, rather than allocating and throwing an exception, when a file is not found. See nodejs/node#33716
Future versions of node will be able to return undefined, rather than allocating and throwing an exception, when a file is not found. See nodejs/node#33716 Co-authored-by: Andrew Casey <amcasey@users.noreply.github.com>
Future versions of node will be able to return undefined, rather than allocating and throwing an exception, when a file is not found. See nodejs/node#33716 Co-authored-by: Andrew Casey <amcasey@users.noreply.github.com>
Future versions of node will be able to return undefined, rather than allocating and throwing an exception, when a file is not found. See nodejs/node#33716 Co-authored-by: Andrew Casey <amcasey@users.noreply.github.com>
Refs: nodejs#33716 Refs: nodejs#35993 Refs: nodejs#35911
Refs: #33716 Refs: #35993 Refs: #35911 PR-URL: #39972 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
PR-URL: #39972 Refs: #33716 Refs: #35993 Refs: #35911 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
Refs: #33716 Refs: #35993 Refs: #35911 PR-URL: #39972 Reviewed-By: Michaël Zasso <targos@protonmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Juan José Arboleda <soyjuanarbol@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Tobias Nießen <tniessen@tnie.de>
For consumers that aren't interested in why a
statSync
call failed,allocating and throwing an exception is an unnecessary expense. This PR
adds an option that will cause it to return
undefined
in such casesinstead.
As a motivating example, the JavaScript & TypeScript language service
shared between Visual Studio and Visual Studio Code is stuck with
synchronous file IO for architectural and backward-compatibility
reasons. It frequently needs to speculatively check for the existence
of files and directories that may not exist (and cares about file vs
directory, so
existsSync
is insufficient), but ignores file systementries it can't access, regardless of the reason.
Benchmarking the language service is difficult because it's so hard to
get good coverage of both code bases and user behaviors, but, as a
representative metric, we measured batch compilation of a few hundred
popular projects (by star count) from GitHub and found that, on average,
we saved about 1-2% of total compilation time. We speculate that the
savings could be even more significant in interactive (language service
or watch mode) scenarios, where the same (non-existent) files need to be
polled over and over again. It's not a huge improvement, but it's a
very small change and it will affect a lot of users (and CI runs).
For reference, our measurements were against
v12.x
(3637a06 at thetime) on an Ubuntu Server desktop with an SSD.
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passes