Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 46 additions & 46 deletions doc/contributing/adding-v8-fast-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,8 @@ for example, they may not trigger garbage collection.
in a snapshot (either the built-in or a user-land one). Please refer to the
[binding functions documentation](../../src/README.md#binding-functions) for more
information.
* To test fast APIs, make sure to run the tests in a loop with a decent
iterations count to trigger relevant optimizations that prefer the fast API
over the slow one.
* In debug mode (`--debug` or `--debug-node` flags), the fast API calls can be
tracked using the `TRACK_V8_FAST_API_CALL("key")` macro. This can be used to
count how many times fast paths are taken during tests. The key is a global
identifier and should be unique across the codebase.
Use `"binding_name.function_name"` or `"binding_name.function_name.suffix"` to
ensure uniqueness.
* Fast API functions must be tested following the example in
[Test with Fast API path](#test-with-fast-api-path).
* The fast callback must be idempotent up to the point where error and fallback
conditions are checked, because otherwise executing the slow callback might
produce visible side effects twice.
Expand Down Expand Up @@ -175,40 +168,47 @@ A typical function that communicates between JavaScript and C++ is as follows.
v8::FastApiCallbackOptions& options);
```

* In the unit tests:

Since the fast API function uses `TRACK_V8_FAST_API_CALL`, we can ensure that
the fast paths are taken and test them by writing tests that force
V8 optimizations and check the counters.

```js
// Flags: --expose-internals --no-warnings --allow-natives-syntax
'use strict';
const common = require('../common');

const { internalBinding } = require('internal/test/binding');
// We could also require a function that uses the internal binding internally.
const { divide } = internalBinding('custom_namespace');

// The function that will be optimized. It has to be a function written in
// JavaScript. Since `divide` comes from the C++ side, we need to wrap it.
function testFastPath(a, b) {
return divide(a, b);
}

eval('%PrepareFunctionForOptimization(testFastPath)');
// This call will let V8 know about the argument types that the function expects.
assert.strictEqual(testFastPath(6, 3), 2);

eval('%OptimizeFunctionOnNextCall(testFastPath)');
assert.strictEqual(testFastPath(8, 2), 4);
assert.throws(() => testFastPath(1, 0), {
code: 'ERR_INVALID_STATE',
});

if (common.isDebug) {
const { getV8FastApiCallCount } = internalBinding('debug');
assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.ok'), 1);
assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.error'), 1);
}
```
### Test with Fast API path

In debug mode (`./configure --debug` or `./configure --debug-node` flags), the
fast API calls can be tracked using the `TRACK_V8_FAST_API_CALL("key")` macro.
This can be used to count how many times fast paths are taken during tests. The
key is a global identifier and should be unique across the codebase.
Use `"binding_name.function_name"` or `"binding_name.function_name.suffix"` to
ensure uniqueness.

In the unit tests, since the fast API function uses `TRACK_V8_FAST_API_CALL`,
we can ensure that the fast paths are taken and test them by writing tests that
force V8 optimizations and check the counters.

```js
// Flags: --expose-internals --no-warnings --allow-natives-syntax
'use strict';
const common = require('../common');

const { internalBinding } = require('internal/test/binding');
// We could also require a function that uses the internal binding internally.
const { divide } = internalBinding('custom_namespace');

// The function that will be optimized. It has to be a function written in
// JavaScript. Since `divide` comes from the C++ side, we need to wrap it.
function testFastPath(a, b) {
return divide(a, b);
}

eval('%PrepareFunctionForOptimization(testFastPath)');
// This call will let V8 know about the argument types that the function expects.
assert.strictEqual(testFastPath(6, 3), 2);

eval('%OptimizeFunctionOnNextCall(testFastPath)');
assert.strictEqual(testFastPath(8, 2), 4);
assert.throws(() => testFastPath(1, 0), {
code: 'ERR_INVALID_STATE',
});

if (common.isDebug) {
const { getV8FastApiCallCount } = internalBinding('debug');
assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.ok'), 1);
assert.strictEqual(getV8FastApiCallCount('custom_namespace.divide.error'), 1);
}
```
Loading