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

src: use object to pass Environment to functions #26382

Closed
wants to merge 1 commit into from

Conversation

addaleax
Copy link
Member

@addaleax addaleax commented Mar 1, 2019

Use a v8::Object with an internal field, rather than a v8::External.

On a GetReturnValue().Set(Environment::GetCurrent(args) == nullptr)
noop function, this benchmarks as a ~60 % speedup, as calls to
obj->GetAlignedPointerFromInternalField() can be inlined.

This also makes breaking up some pieces of the Environment class
into per-native-binding data easier, if we want to pursue that path
in the future.

Checklist
  • make -j4 test (UNIX), or vcbuild test (Windows) passes
  • commit message follows commit guidelines

Use a `v8::Object` with an internal field, rather than a
`v8::External`.

On a `GetReturnValue().Set(Environment::GetCurrent(args) == nullptr)`
noop function, this benchmarks as a ~60 % speedup, as calls to
`obj->GetAlignedPointerFromInternalField()` can be inlined.

This also makes breaking up some pieces of the `Environment` class
into per-native-binding data easier, if we want to pursue that path
in the future.
@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. labels Mar 1, 2019
@addaleax
Copy link
Member Author

addaleax commented Mar 1, 2019

CI: https://ci.nodejs.org/job/node-test-pull-request/21107/ (:heavy_check_mark:)

Copy link
Member

@bnoordhuis bnoordhuis left a comment

Choose a reason for hiding this comment

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

calls to obj->GetAlignedPointerFromInternalField() can be inlined

I suspect the reason of the performance improvement is less indirection.

A v8::External is very similar to a v8::Object but it boxes the pointer inside a v8::internal::Foreign instance in order that unaligned pointers can be stored. That additional indirection comes at a cost.

I suppose V8 could optimize the box away most of the time by having two different External types internally, one for unboxed aligned pointers and one for boxed unaligned pointers. But I digress.

@addaleax addaleax added the author ready PRs that have at least one approval, no pending requests for changes, and a CI started. label Mar 2, 2019
@addaleax
Copy link
Member Author

addaleax commented Mar 2, 2019

@bnoordhuis Yeah, that makes sense.

@addaleax
Copy link
Member Author

addaleax commented Mar 5, 2019

Landed in 955be86

@addaleax addaleax closed this Mar 5, 2019
@addaleax addaleax deleted the env-getcurrent-args branch March 5, 2019 00:00
addaleax added a commit that referenced this pull request Mar 5, 2019
Use a `v8::Object` with an internal field, rather than a
`v8::External`.

On a `GetReturnValue().Set(Environment::GetCurrent(args) == nullptr)`
noop function, this benchmarks as a ~60 % speedup, as calls to
`obj->GetAlignedPointerFromInternalField()` can be inlined and
the field is stored with one level of indirection less.

This also makes breaking up some pieces of the `Environment` class
into per-native-binding data easier, if we want to pursue that path
in the future.

PR-URL: #26382
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
BridgeAR pushed a commit to BridgeAR/node that referenced this pull request Mar 12, 2019
Use a `v8::Object` with an internal field, rather than a
`v8::External`.

On a `GetReturnValue().Set(Environment::GetCurrent(args) == nullptr)`
noop function, this benchmarks as a ~60 % speedup, as calls to
`obj->GetAlignedPointerFromInternalField()` can be inlined and
the field is stored with one level of indirection less.

This also makes breaking up some pieces of the `Environment` class
into per-native-binding data easier, if we want to pursue that path
in the future.

PR-URL: nodejs#26382
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
@joyeecheung
Copy link
Member

This is currently blocking the serialization of Environment, because the FunctionTemplates we create reference the callback data that are now context-dependent, but templates should be context-independent (that is, we now reference context-dependent objects from context-independent objects).

To unblock the snapshot integration, we need to go back to v8::External - should we wait until the performance diff is addressed in the upstream? I could try #26382 (review) and see if that solves the performance issue first.

@addaleax
Copy link
Member Author

@joyeecheung So, I’m unfortunately only seeing your comment now, but I think it still deserves a reply.

What you’re saying sounds like a defect in the V8 API to me. If templates are supposed to be context-independent, and the only way to associate data with C++ binding callbacks is to pass a Local<Value>, then that doesn’t leave much room for what kind of data can be associated with the callback. Rather than going back to v8::External and improving performance there, one possibility I’d prefer is the ability to pass a more general Local<Data>, which can also be an ObjectTemplate, and then fill the internal slots of those object template’s instances after the functions have been instantiated.

As the commit message hints at, my main motivation here wasn’t actually the performance improvement, but rather I was trying to open up the possibility to make our internals a bit more flexible by allowing associating more than one item of data with the callback. (I’m assuming that that can also be done another way, using a v8::External and an additional layer of indirection if we so choose.) I had a PoC written up for that, and I think that might now turn out to be useful for addressing #28823 – feel free to ping me somewhere if you’d like to talk more about this, it’s definitely not something I had in mind when originally writing this PR.

@joyeecheung
Copy link
Member

joyeecheung commented Dec 28, 2019

I’d prefer is the ability to pass a more general Local, which can also be an ObjectTemplate, and then fill the internal slots of those object template’s instances after the functions have been instantiated.

As long as what we have in ENVIRONMENT_STRONG_PERSISTENT_TEMPLATES and what they hold reference to as data are really context-independent we should be good here. Although with Local<Data> as the data we still need to be careful that they should be context-independent or otherwise we still cannot add them to the snapshot.

I was trying to open up the possibility to make our internals a bit more flexible by allowing associating more than one item of data with the callback.

It's difficult for me to see how allowing a Local<Data> instead of a Local<Value> makes a difference here...? If that data associated with the template consists of JS objects, then it's still context-dependent and would still crash the snapshot creator when we traverse them from the supposedly context-independent templates. If that data associated with the template consists of just blobs of non-JS data then I suppose composing them into a C++ construct that gets referenced by v8::External should be already enough..

I'll rebase my snapshot prototype and post it here for testing when I am back from vacation - the last time I tried, after I reverted this PR it only crashed until after the templates were traversed in the snapshot creator (due to something else I needed to fix..that I could not remember). With this PR it crashed during the traversal of the templates. So if your POC works it should once again crash at some point later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
author ready PRs that have at least one approval, no pending requests for changes, and a CI started. c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants