-
Notifications
You must be signed in to change notification settings - Fork 30k
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
n-api: Potential N-API performance optimizations #14379
Comments
|
N-API already uses a wrapper around |
Reading the V8 source code, that doesn’t seem to be true: Lines 1332 to 1337 in 7fdcb68
Regarding 2: It’s probably easiest to just add if (val->IsInt32()) {
*result = val.As<Int32>()->Value();
return napi_clear_last_error(env);
} even before the |
Yes, you're right Somehow I had overlooked |
Two more... 4. Over-use of
|
Ref: #14379 PR-URL: #14393 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Jason Ginchereau <[email protected]>
Ref: #14379 PR-URL: #14393 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Jason Ginchereau <[email protected]>
- Add separate APIs for creating different kinds of numbers, because creating a V8 number value from an integer is faster than creating one from a double. - When getting number values, avoid getting the current context because the context will not actually be used and is expensive to obtain. - When creating values, don't use v8::TryCatch (NAPI_PREAMBLE), because these functions have no possibility of executing JS code. Refs: nodejs#14379
- Add separate APIs for creating different kinds of numbers, because creating a V8 number value from an integer is faster than creating one from a double. - When getting number values, avoid getting the current context because the context will not actually be used and is expensive to obtain. - When creating values, don't use v8::TryCatch (NAPI_PREAMBLE), because these functions have no possibility of executing JS code. Refs: nodejs#14379 PR-URL: nodejs#14573 Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
- Add separate APIs for creating different kinds of numbers, because creating a V8 number value from an integer is faster than creating one from a double. - When getting number values, avoid getting the current context because the context will not actually be used and is expensive to obtain. - When creating values, don't use v8::TryCatch (NAPI_PREAMBLE), because these functions have no possibility of executing JS code. Refs: #14379 PR-URL: #14573 Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
Ref: nodejs#14379 PR-URL: nodejs#14393 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Jason Ginchereau <[email protected]>
- Add separate APIs for creating different kinds of numbers, because creating a V8 number value from an integer is faster than creating one from a double. - When getting number values, avoid getting the current context because the context will not actually be used and is expensive to obtain. - When creating values, don't use v8::TryCatch (NAPI_PREAMBLE), because these functions have no possibility of executing JS code. Refs: nodejs#14379 PR-URL: nodejs#14573 Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
Ref: #14379 Backport-PR-URL: #19447 PR-URL: #14393 Reviewed-By: Ben Noordhuis <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Jason Ginchereau <[email protected]>
- Add separate APIs for creating different kinds of numbers, because creating a V8 number value from an integer is faster than creating one from a double. - When getting number values, avoid getting the current context because the context will not actually be used and is expensive to obtain. - When creating values, don't use v8::TryCatch (NAPI_PREAMBLE), because these functions have no possibility of executing JS code. Refs: #14379 Backport-PR-URL: #19447 PR-URL: #14573 Reviewed-By: Timothy Gu <[email protected]> Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
I believe we have addressed all points except 3). |
There's not much more we can do, and we've covered most of these points. Closing. |
Was |
@jorangreef looks like |
For GCC and Clang, applying |
|
I analyzed the performance of a native module that was converted to N-API using a profiling tool, and compared the results to the original module that used V8 APIs. While overall the overhead of N-API is fairly minimal already, I did manage to identify 3 potential optimizations. Each of these can substantially reduce the impact of N-API in certain scenarios. I have tested an early version of these fixes already to confirm that, but I wanted to give a chance for discussion before I submit a PR.
1. Creating integer values
N-API currently offers only one way to create a number value:
V8 has an optimized representation for 32-bit integer values, but because the value provided to N-API is always a double it always calls
v8::Number::New()
(neverv8::Integer::New
), so it does not create the optimal integer representation. Therefore these integer values are slower to create and slower to work with than they could be.Instead of a single
napi_create_number()
API, there should probably be one for each of:int32_t
,uint32_t
,int64_t
,double
. Note there are alreadynapi_get_value_*()
functions for each of those 4 types, so having the same 4napi_create_*()
variants is more natural anyway.2. Getting integer values
The N-API functions that get integer values do some work to get a
v8::Context
that is never actually used. The profiler data showed that the call tov8::Isolate::GetCurrentContext()
is actually somewhat expensive. (And it is apparently not optimized out by the compiler.)The implementation of
napi_get_value_int32()
includes this code:But
v8::Value::Int32Value()
does not use thecontext
argument when the value is a number type (a condition that was already checked above):I can think of two ways to make this faster:
v8::Value::Int32Value()
overload that does not take a context (and does not return a maybe). The problem is it is marked as "to be deprecated soon".v8::Local<v8::Context>
value tov8::Value::Int32Value()
. This relies on the internal implementation detail that it does not use the context when the value is a number type. But in practice it should be safe, and will be easily caught by tests in the unlikely event V8 ever changes that API behavior.I also considred caching the
v8::Context
in thenapi_env
structure, but that probably isn't valid because APIs can be called from different context scopes.3. Allocating handle scopes
V8 handle scopes are normally stack-allocated. But the current N-API implementation puts them on the heap, which means every entry/exit of a scope involves expensive
new
anddelete
operations.I can think of two ways to make this faster:
napi_env
. Track which ones are used/freed, and allocate new handle scopes on the heap only if the pre-allocated ones are all in use.The text was updated successfully, but these errors were encountered: