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: replace all C++ promises with JS promises #20830

Closed
wants to merge 1 commit into from

Conversation

BridgeAR
Copy link
Member

C++ promises can not be properly optimized by V8. They also behave
a tiny bit different than "regular" promises (they can not be monkey patched).

This was requested by @nodejs/v8.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • tests and/or benchmarks are included
  • documentation is changed or added
  • commit message follows commit guidelines

@nodejs-github-bot nodejs-github-bot added the lib / src Issues and PRs related to general changes in the lib or src directory. label May 18, 2018
@BridgeAR
Copy link
Member Author

@BridgeAR BridgeAR added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label May 19, 2018
@TimothyGu
Copy link
Member

This was requested by @nodejs/v8.

Can you link where as a "Refs" in the commit message?

@BridgeAR
Copy link
Member Author

@TimothyGu we discussed this in person (even though we have meeting notes) two weeks ago.

@TimothyGu
Copy link
Member

They also behave a tiny bit different than "regular" promises (they can not be monkey patched).

Isn't this a desirable property?

@BridgeAR
Copy link
Member Author

@TimothyGu not really. Monkey patching is for example still important for APMs and there are probably a couple use cases where people rely on being able to monkey patch promises. Not being able to monkey patch those goes AFAIK against the spec.

@TimothyGu
Copy link
Member

Monkey patching is for example still important for APMs

Async Hooks are also important to APMs, and monkey patching Promise would not allow PROMISE hooks from being called as expected. (By saying this I'm not saying not monkeypatching is better for APMs – only APM developers can say for sure. I am saying this should not be listed as one of the pros for allowing monkeypatching.)

Not being able to monkey patch those goes AFAIK against the spec.

There is no spec for util.promisify, except the documentation and our previous behavior, the latter of which this PR is now breaking.

@devsnek
Copy link
Member

devsnek commented May 19, 2018

a happy medium that can let changing these to be monkey-patchable in a separate pr could be using SafePromise from internal/safe_globals

@TimothyGu
Copy link
Member

@devsnek We don't want to expose any Safe* objects to userland, as it would no longer be safe. Simply caching the Promise constructor at startup should suffice.

@benjamingr
Copy link
Member

First - do we have benchmarks for this?

Second - monkey patching won’t work because of async functions anyway - the future for diagnostics is better hooks

Otherwise LGTM

} else {
promiseResolve(promise, { stdout, stderr });
}
return new Promise((resolve, reject) => {
Copy link
Member

Choose a reason for hiding this comment

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

It was written that way to avoid allocation of a closure and be speed-competitive with bluebird.

I’d like to make sure that the speed remains as fast

Copy link
Member

Choose a reason for hiding this comment

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

Yes, we should measure the performance here.

I was suggesting to start with JS promises instead, since we spend some time recently to even inline the promise constructor into TurboFan optimized code. If that turns out to be too slow, then the other alternative is to use the createPromise and friends from v8-extras, which is also known to TurboFan, and significantly faster than going through C++.

Copy link
Member

Choose a reason for hiding this comment

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

+1 for createPromise from v8-extras.

promiseReject(promise, err);
}
return promise;
return new Promise((resolve, reject) => {

This comment was marked as outdated.

This comment was marked as outdated.

Copy link
Member

@bmeurer bmeurer left a comment

Choose a reason for hiding this comment

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

LGTM if performance is okay.

Copy link
Member

@benjamingr benjamingr left a comment

Choose a reason for hiding this comment

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

I'm only blocking to make sure no one merges this by accident before benchmarks.

Copy link
Member

@apapirovski apapirovski left a comment

Choose a reason for hiding this comment

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

This will be somewhere around 10-30% slower than the current version. I tested this fairly recently in relation to promisify. I'll leave it up to others to decide whether that's acceptable.

I chose to discard my own PR due to how much slower it was but perhaps others have a different opinion.

@benjamingr
Copy link
Member

benjamingr commented May 19, 2018

@apapirovski

This will be somewhere around 10-30% slower than the current version. I tested this fairly recently in relation to promisify. I'll leave it up to others to decide whether that's acceptable.

A 30% slower promisify would beat half the point of having promisify itself in core. While I personally find it a useful utility anyway the speed benefit was a big part of the reason people were convinced to add it.

That is, if this PR is significantly slower then I'm very -1 on it. However if the V8 team can make an in-javascript promisify as fast as the current version then I'm +1 on it - and in any case a v8-extras solution should be considered.

@jasnell
Copy link
Member

jasnell commented May 19, 2018

@BridgeAR ... would prefer the changes to different modules to at least be in separate commits.

@BridgeAR
Copy link
Member Author

@jasnell if this will land at all I would like to keep it in a single commit. I do not see a compelling reason for it to be split into smaller chunks.

@apapirovski do you still have the benchmark code you used? Any performance impact is heavily influenced by the task at hand. So benchmarking a promisified setTimeout should show the biggest impact if it has influence on the performance.


A general thing: I feel we block to many things for reasons that will have little to no effect on real world applications.

@jasnell
Copy link
Member

jasnell commented May 19, 2018

The compelling reason is to isolate distinct changes in case they need to be reverted or backported separately for any reason. It also makes the changes easier to review module by module.

@addaleax
Copy link
Member

@jasnell I think this is fine to land as a single commit – it’s not particularly huge or so, and I don’t think we want to backport any of this anyway because of the perf impact (adding dont-land-on labels).

@@ -2731,11 +2730,9 @@ function connect(authority, options, listener) {
// Support util.promisify
Object.defineProperty(connect, promisify.custom, {
value: (authority, options) => {
const promise = createPromise();
const server = connect(authority,
Copy link
Member

@TimothyGu TimothyGu Jun 26, 2018

Choose a reason for hiding this comment

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

Actually, it seems we might be able to remove the custom promisifier entirely: what the getter is doing right now should be the exact things done by util.promisify.

Copy link
Member Author

Choose a reason for hiding this comment

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

A test fails if that part is removed.

C++ promises can not be properly optimized by V8. They also behave
a tiny bit different than "regular" promises.
@BridgeAR
Copy link
Member Author

BridgeAR commented Jul 14, 2018

@TimothyGu
Copy link
Member

@BridgeAR Can you file a follow-up issue on #20830 (comment) when this PR gets landed?

@BridgeAR
Copy link
Member Author

Landed in bd1f355 🎉

BridgeAR added a commit to BridgeAR/node that referenced this pull request Jul 16, 2018
C++ promises can not be properly optimized by V8. They also behave
a tiny bit different than "regular" promises.

PR-URL: nodejs#20830
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@BridgeAR BridgeAR closed this Jul 16, 2018
BridgeAR added a commit to BridgeAR/node that referenced this pull request Jul 16, 2018
This is just a small refactoring.

Refs: nodejs#20830 (comment)
@BridgeAR BridgeAR mentioned this pull request Jul 16, 2018
4 tasks
@BridgeAR
Copy link
Member Author

@TimothyGu I personally can not see how that custom promisify should be removed. It is a wrapper for an event and the first argument is always going to be the server. Therefore the http2 case relies on the return value of the function and not on the callback value as that would be registered as an error.
However, I saw that the function could be minified further and I did that: #21835

If there is something else you had in mind, please let me know!

@targos targos added backport-requested-v10.x and removed author ready PRs that have at least one approval, no pending requests for changes, and a CI started. labels Jul 16, 2018
@targos
Copy link
Member

targos commented Jul 16, 2018

Should this be backported to v10.x-staging? If yes please follow the guide and raise a backport PR, if not let me know or add the dont-land-on label.

targos pushed a commit to targos/node that referenced this pull request Jul 19, 2018
C++ promises can not be properly optimized by V8. They also behave
a tiny bit different than "regular" promises.

PR-URL: nodejs#20830
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
targos pushed a commit that referenced this pull request Jul 24, 2018
C++ promises can not be properly optimized by V8. They also behave
a tiny bit different than "regular" promises.

PR-URL: #20830
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Gus Caplan <me@gus.host>
Reviewed-By: Tiancheng "Timothy" Gu <timothygu99@gmail.com>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>

Backport-PR-URL: #21879
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
@targos targos mentioned this pull request Jul 31, 2018
@BridgeAR BridgeAR deleted the replace-c-promises branch January 20, 2020 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
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. semver-minor PRs that contain new features and should be released in the next minor version.
Projects
None yet
Development

Successfully merging this pull request may close these issues.