Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 1d44017

Browse filesBrowse files
anonrigruyadorno
authored andcommitted
doc: add v8 fast api contribution guidelines
PR-URL: #46199 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Robert Nagy <ronagy@icloud.com> Reviewed-By: Darshan Sen <raisinten@gmail.com> Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 640d111 commit 1d44017
Copy full SHA for 1d44017

File tree

Expand file treeCollapse file tree

2 files changed

+154
-0
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

2 files changed

+154
-0
lines changed
Open diff view settings
Collapse file
+150Lines changed: 150 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# Adding V8 Fast API
2+
3+
Node.js uses [V8](https://v8.dev/) as its JavaScript engine.
4+
Embedding functions implemented in C++ incur a high overhead, so V8
5+
provides an API to implement native functions which may be invoked directly
6+
from JITted code. These functions also come with additional constraints,
7+
for example, they may not trigger garbage collection.
8+
9+
## Limitations
10+
11+
* Fast API functions may not trigger garbage collection. This means by proxy
12+
that JavaScript execution and heap allocation are also forbidden, including
13+
`v8::Array::Get()` or `v8::Number::New()`.
14+
* Throwing errors is not available from within a fast API call, but can be done
15+
through the fallback to the slow API.
16+
* Not all parameter and return types are supported in fast API calls.
17+
For a full list, please look into
18+
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h).
19+
20+
## Requirements
21+
22+
* Any function passed to `CFunction::Make`, including fast API function
23+
declarations, should have their signature registered in
24+
[`node_external_reference.h`](../../src/node_external_reference.h) file.
25+
Although, it would not start failing or crashing until the function ends up
26+
in a snapshot (either the built-in or a user-land one). Please refer to the
27+
[binding functions documentation](../../src#binding-functions) for more
28+
information.
29+
* To test fast APIs, make sure to run the tests in a loop with a decent
30+
iterations count to trigger relevant optimizations that prefer the fast API
31+
over the slow one.
32+
* The fast callback must be idempotent up to the point where error and fallback
33+
conditions are checked, because otherwise executing the slow callback might
34+
produce visible side effects twice.
35+
36+
## Fallback to slow path
37+
38+
Fast API supports fallback to slow path for when it is desirable to do so,
39+
for example, when throwing a custom error or executing JavaScript code is
40+
needed. The fallback mechanism can be enabled and changed from the C++
41+
implementation of the fast API function declaration.
42+
43+
Passing `true` to the `fallback` option will force V8 to run the slow path
44+
with the same arguments.
45+
46+
In V8, the options fallback is defined as `FastApiCallbackOptions` inside
47+
[`v8-fast-api-calls.h`](../../deps/v8/include/v8-fast-api-calls.h).
48+
49+
* C++ land
50+
51+
Example of a conditional fast path on C++
52+
53+
```cpp
54+
// Anywhere in the execution flow, you can set fallback and stop the execution.
55+
static double divide(const int32_t a,
56+
const int32_t b,
57+
v8::FastApiCallbackOptions& options) {
58+
if (b == 0) {
59+
options.fallback = true;
60+
return 0;
61+
} else {
62+
return a / b;
63+
}
64+
}
65+
```
66+
67+
## Example
68+
69+
A typical function that communicates between JavaScript and C++ is as follows.
70+
71+
* On the JavaScript side:
72+
73+
```js
74+
const { divide } = internalBinding('custom_namespace');
75+
```
76+
77+
* On the C++ side:
78+
79+
```cpp
80+
#include "v8-fast-api-calls.h"
81+
82+
namespace node {
83+
namespace custom_namespace {
84+
85+
static void SlowDivide(const FunctionCallbackInfo<Value>& args) {
86+
Environment* env = Environment::GetCurrent(args);
87+
CHECK_GE(args.Length(), 2);
88+
CHECK(args[0]->IsInt32());
89+
CHECK(args[1]->IsInt32());
90+
auto a = args[0].As<v8::Int32>();
91+
auto b = args[1].As<v8::Int32>();
92+
93+
if (b->Value() == 0) {
94+
return node::THROW_ERR_INVALID_STATE(env, "Error");
95+
}
96+
97+
double result = a->Value() / b->Value();
98+
args.GetReturnValue().Set(v8::Number::New(env->isolate(), result));
99+
}
100+
101+
static double FastDivide(const int32_t a,
102+
const int32_t b,
103+
v8::FastApiCallbackOptions& options) {
104+
if (b == 0) {
105+
options.fallback = true;
106+
return 0;
107+
} else {
108+
return a / b;
109+
}
110+
}
111+
112+
CFunction fast_divide_(CFunction::Make(FastDivide));
113+
114+
static void Initialize(Local<Object> target,
115+
Local<Value> unused,
116+
Local<Context> context,
117+
void* priv) {
118+
SetFastMethod(context, target, "divide", SlowDivide, &fast_divide_);
119+
}
120+
121+
void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
122+
registry->Register(SlowDivide);
123+
registry->Register(FastDivide);
124+
registry->Register(fast_divide_.GetTypeInfo());
125+
}
126+
127+
} // namespace custom_namespace
128+
} // namespace node
129+
130+
NODE_BINDING_CONTEXT_AWARE_INTERNAL(custom_namespace,
131+
node::custom_namespace::Initialize);
132+
NODE_BINDING_EXTERNAL_REFERENCE(
133+
custom_namespace,
134+
node::custom_namespace::RegisterExternalReferences);
135+
```
136+
137+
* Update external references ([`node_external_reference.h`](../../src/node_external_reference.h))
138+
139+
Since our implementation used
140+
`double(const int32_t a, const int32_t b, v8::FastApiCallbackOptions& options)`
141+
signature, we need to add it to external references and in
142+
`ALLOWED_EXTERNAL_REFERENCE_TYPES`.
143+
144+
Example declaration:
145+
146+
```cpp
147+
using CFunctionCallbackReturningDouble = double (*)(const int32_t a,
148+
const int32_t b,
149+
v8::FastApiCallbackOptions& options);
150+
```
Collapse file

‎src/README.md‎

Copy file name to clipboardExpand all lines: src/README.md
+4Lines changed: 4 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ embedder API.
3131
Important concepts when using V8 are the ones of [`Isolate`][]s and
3232
[JavaScript value handles][].
3333

34+
V8 supports [fast API calls][], which can be useful for improving the
35+
performance in certain cases.
36+
3437
## libuv API documentation
3538

3639
The other major dependency of Node.js is [libuv][], providing
@@ -1055,6 +1058,7 @@ static void GetUserInfo(const FunctionCallbackInfo<Value>& args) {
10551058
[cleanup hooks]: #cleanup-hooks
10561059
[event loop]: #event-loop
10571060
[exception handling]: #exception-handling
1061+
[fast API calls]: ../doc/contributing/adding-v8-fast-api.md
10581062
[internal field]: #internal-fields
10591063
[introduction for V8 embedders]: https://v8.dev/docs/embed
10601064
[libuv]: https://libuv.org/

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.