-
Notifications
You must be signed in to change notification settings - Fork 29.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
doc: add v8 fast api contribution guidelines
- Loading branch information
Showing
1 changed file
with
150 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,150 @@ | ||
# Adding V8 Fast API | ||
|
||
Node.js uses [V8](https://github.com/v8/v8) as the JavaScript engine. | ||
In order to provide fast paths for functions that are called quite often, | ||
V8 proposes the usage of `fast api calls` that does not use any of the V8 | ||
internals, but uses internal C functions. | ||
|
||
## Limitations | ||
|
||
* Fast API functions can not use JavaScript heap allocation. | ||
* Fast API functions can not trigger JavaScript execution. | ||
* Reporting/returning errors is not available on fast API, but can be done | ||
through fallbacks to slow API. | ||
* Not all parameter and return types are supported in fast API calls. | ||
For a full list, please look into | ||
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h) file. | ||
|
||
## Requirements | ||
|
||
* Each unique fast API function signature should be defined inside the | ||
[`external references`](../../src/node_external_reference.h) file. | ||
* To test fast APIs, make sure to run the tests in a loop with a decent | ||
iterations count to trigger V8 for optimization and to prefer the fast API | ||
over slow one. | ||
|
||
## Fallback to slow path | ||
|
||
Fast API supports fallback to slow path (implementation that uses V8 internals) | ||
in case logically it is wise to do so, for example when providing a more | ||
detailed error. Fallback mechanism can be enabled, and changed from both caller | ||
JavaScript function or from the fast API function declaration. | ||
|
||
Every fast API function accessible from JavaScript side can pass an object | ||
consisting of fallback key with a boolean value as the last parameter | ||
(or the first parameter, if no parameters of the function exist). | ||
|
||
In V8 the options fallback is defined as `FastApiCallbackOptions` inside | ||
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h) file. | ||
|
||
* JavaScript land | ||
|
||
Example of a JavaScript call into a fast API: | ||
|
||
```js | ||
// Let divide be a function that provides fast api calls. | ||
const { divide } = internalBinding('custom_namespace'); | ||
|
||
function divide(a, b) { | ||
return divide(a, b, { fallback: a === 0 }) | ||
} | ||
``` | ||
|
||
* C++ land | ||
|
||
Example of a conditional fast path on C++ | ||
|
||
```cpp | ||
// Anywhere in the execution flow, you can set fallback and stop the execution. | ||
static double divide(const int32_t a, | ||
const int32_t b, | ||
v8::FastApiCallbackOptions& options) { | ||
if (b == 0) { | ||
options.fallback = true; | ||
} else { | ||
return a / b; | ||
} | ||
} | ||
``` | ||
|
||
## Example | ||
|
||
A typical function that communicates between JavaScript and C++ is as follows. | ||
|
||
* On the JavaScript side: | ||
|
||
```js | ||
const { divide } = internalBinding('custom_namespace'); | ||
``` | ||
|
||
* On the C++ side: | ||
|
||
```cpp | ||
#include "v8-fast-api-calls.h" | ||
|
||
namespace node { | ||
namespace custom_namespace { | ||
|
||
static void divide(const FunctionCallbackInfo<Value>& args) { | ||
Environment* env = Environment::GetCurrent(args); | ||
CHECK(args[0]->IsInt32()); | ||
CHECK(args[1]->IsInt32()); | ||
auto a = args[0].As<v8::Int32>(); | ||
auto b = args[1].As<v8::Int32>(); | ||
|
||
if (b->Value() == 0) { | ||
return node::THROW_ERR_INVALID_STATE(env, "Error"); | ||
} | ||
|
||
double result = a->Value() / b->Value(); | ||
args.GetReturnValue().Set(result); | ||
} | ||
|
||
static double FastDivide(const int32_t a, | ||
const int32_t b, | ||
v8::FastApiCallbackOptions& options) { | ||
if (b == 0) { | ||
options.fallback = true; | ||
} else { | ||
return a / b; | ||
} | ||
} | ||
|
||
CFunction fast_divide_(CFunction::Make(FastDivide)); | ||
|
||
static void Initialize(Local<Object> target, | ||
Local<Value> unused, | ||
Local<Context> context, | ||
void* priv) { | ||
SetFastMethod(context, target, "divide", Divide, &fast_divide_); | ||
} | ||
|
||
void RegisterExternalReferences(ExternalReferenceRegistry* registry) { | ||
registry->Register(Divide); | ||
registry->Register(FastDivide); | ||
registry->Register(fast_divide_.GetTypeInfo()); | ||
} | ||
|
||
} // namespace custom_namespace | ||
} // namespace node | ||
|
||
NODE_BINDING_CONTEXT_AWARE_INTERNAL(custom_namespace, | ||
node::custom_namespace::Initialize); | ||
NODE_BINDING_EXTERNAL_REFERENCE( | ||
custom_namespace, | ||
node::custom_namespace::RegisterExternalReferences); | ||
``` | ||
* Update external references ([`node_external_reference.h`](../../src/node_external_reference.h)) | ||
Since our implementation used | ||
`double(const int32_t a, const int32_t b, v8::FastApiCallbackOptions& options)` | ||
signature, we need to add it to external references. | ||
|
||
Example declaration: | ||
|
||
```cpp | ||
using CFunctionCallbackReturningDouble = double (*)(const int32_t a, | ||
const int32_t b, | ||
v8::FastApiCallbackOptions& options); | ||
``` |