diff --git a/doc/contributing/adding-v8-fast-api.md b/doc/contributing/adding-v8-fast-api.md new file mode 100644 index 00000000000000..0b93f95b47d2fd --- /dev/null +++ b/doc/contributing/adding-v8-fast-api.md @@ -0,0 +1,119 @@ +# 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 calls can not use `V8` internals inside the fast path. +- Not all parameter and return types are supported in fast api calls. +For a full list, please look into `v8-fast-api-calls.h` file. + +## Requirements +- Each unique fast path function signature should be defined inside +`node_external_reference.h` file. +- In order to test fast paths, make sure to run the tests in a loop +with more than 1000 iterations to force V8 to optimize and prefer the +fast api over slow path. + +## Fallback to slow path + +Fast apis support fallback to slow path (implementation that uses V8 internals) +in case logically it is wise to do so. 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` file. + +- JavaScript land + +Example of a JavaScript: + +```javascript +// Let calculateX be a function that provides fast api calls. +const { calculateX } = internalBinding('custom_namespace'); + +function conditionallyGetX(useSlowPath) { + return calculateX({ fallback: useSlowPath }) +} +``` + +- C++ land + +Example of a conditional fast path on C++ + +```c++ +// Anywhere in the execution flow, you can set fallback and stop the execution. +static void FastCalculateX(const v8::FastApiCallbackOptions& options) { + if (true) { + options.fallback = true; + } +} +``` + +## Example + +A typical function that communicates between JavaScript and C++ is as follows. + +- On the JavaScript side: + +```javascript +const { calculateX } = internalBinding('custom_namespace'); +``` + +- On the C++ side: + +```c++ +namespace node { +namespace custom_namespace { + +#define PREFER_FALLBACK = false; + +static void CalculateX(const FunctionCallbackInfo& args) { + int calculated_x = 5; + args.GetReturnValue().Set(calculated_x); +} + +static int FastCalculateX(const v8::FastApiCallbackOptions& options) { + if (PREFER_FALLBACK) { + options.fallback = true; + return; + } + return 5; +} + +CFunction fast_calculate_x_(CFunction::Make(FastCalculateX)); + +static void Initialize(Local target, Local unused, Local context, void* priv) { + SetFastMethod(context, target, "calculateX", CalculateX, &calculate_x_); +} + +void RegisterExternalReferences(ExternalReferenceRegistry* registry) { + registry->Register(CalculateX); + registry->Register(FastCalculateX); + registry->Register(fast_calculate_x_.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`) + +Since our implementation used `int(const v8::FastApiCallbackOptions& options)` signature, +we need to add it to external references. + +Example declaration: + +```c++ +using CFunctionCallbackReturningInt = int (*)(const v8::FastApiCallbackOptions& options); +```