-
-
Notifications
You must be signed in to change notification settings - Fork 59
V8 Fast API assessment
A code-level assessment of V8's Fast API (v8-fast-api-calls.h) as a potential optimization vector for node-re2. Recorded 2026-05-12. Conclusion: not a fit today for any node-re2 use case, for reasons that compound across availability, type support, and stability stance. The boundary-shift performance intent is real and pursued via N-API levers instead — see discussions/#218 and the project queue.
V8 provides a mechanism by which TurboFan / Maglev can compile JS call sites that invoke registered C++ functions into direct machine-code calls, bypassing the normal v8::FunctionCallback overhead. The introduction blog post (yagiz.co, January 2023) framed it as removing "the high overhead of traditional embedder functions" — measured in tens of nanoseconds per call.
The mechanism: a C++ function registered with a Fast-API signature can be invoked from JIT-compiled JS code without entering V8's runtime. Inputs are passed in C types (int32_t, double, v8::FastOneByteString&, etc.); the runtime synthesises a slow-path fallback of the same function for cases where the fast path cannot be taken.
The relevant V8 header is v8-fast-api-calls.h; Node's own internal contributing doc is adding-v8-fast-api.md.
Per the Node internal-bindings documentation and the v8-dev April 2025 thread:
Arguments:
- Primitives:
bool,int32_t,uint32_t,int64_t,uint64_t,float,double -
v8::Local<v8::Value>— any JS value, but typically loses the fast-path benefit because the C++ has to inspect/unwrap -
v8::FastOneByteString&— the only fast string type. Verbatim from the Node doc: "only allows sequential one-byte strings, which is often not useful" - External pointers (opaque)
Returns: integers, floats, void, pointer-as-External. No Local<Value> returns. Andreas Haas (V8) explained in the v8-dev thread that a Local<Value> lives in a HandleScope that closes when the function returns, and forcing the scope open would defeat the speed purpose. No roadmap to add object/string returns.
TypedArray: support has been deprecated due to bugs. Workaround per the v8-dev thread: use External pointers for FFI instead. The path "pass a TypedArray of UTF-8 bytes, return a TypedArray of result bytes" — the natural shape for RE2 work — does not exist as a fast call today.
Fast API is implemented in V8 and used internally by Node.js core. It is not exposed to external addons through any officially supported path:
-
Node-API does not expose Fast API. No
napi_*symbols for registering fast calls; nov8-fast-api-calls.hconsumption fromnode_api.h. -
v8-fast-api-calls.his not shipped in the addon headers directory. nodejs/node#55725 ("Missing v8-fast-api-calls.h header & FastApiTypedArray symbols", opened Nov 2024) was closed as not-planned with no maintainer commitment to ship the header to addons. - nan technically exposes raw V8 headers but does not wrap Fast API; using it requires writing direct V8 code and patching Node to ship the missing header.
Net: Fast API is a Node-core-internal-bindings feature today. Both addon APIs we'd consider (nan, Node-API) sit outside it.
| Signal | State |
|---|---|
| V8 team stance (Andreas Haas, v8-dev April 2025) | "still considered unstable", "APIs are out-dated and would benefit from cleanup" |
| Node-side champion ronag — Fast-API commits | last on 2024-09-05 ("buffer: re-enable Fast API for Buffer.write after fixing UTF8 handling"); no Fast-API commits since |
| Node-side champion Yagiz Nizipli | still active in 2025, but his April 2025 v8-dev post is essentially "is this stable yet?" |
| Addon-facing header export (nodejs/node#55725) | closed not-planned, Nov 2024 |
| TypedArray fast support | deprecated |
| New addon-facing API | none announced |
Net read: stagnant for the addon use case. Node core continues to opportunistically wire Fast API into hot paths (Buffer write, indexOf, etc.); the addon-facing surface has moved backwards (TypedArray deprecated) and forwards (V8 12.6 lifted some HandleScope / exception constraints) at roughly the same rate. No commitment to public-API stability.
Four compounding blockers.
- API path missing. Neither nan nor N-API exposes Fast API. The header is not shipped to addons. #55725 closed not-planned. Using Fast API would require patching Node or shipping a forked binding system — outside scope.
-
Strings don't match. node-re2 inputs are arbitrary Unicode. JS strings are V8-internal Latin-1 (one-byte) or UTF-16 (two-byte); RE2 wants UTF-8. The only fast-path string type,
FastOneByteString, accepts only sequential Latin-1 — and even then does not help with the UTF-8 conversion that dominates per-call cost for non-ASCII data. - Buffer-as-input path closed. The natural shape for RE2 input is "pointer + length to bytes" — TypedArray or Buffer. TypedArray fast support is deprecated; the only remaining path is External pointers, which requires pre-registering every input buffer as an External — defeats the purpose for ad-hoc inputs.
-
Return types don't match. node-re2 methods return strings, arrays of strings, or RegExp-match-style objects — none are returnable through Fast API. A
test()-styleboolreturn is the only method that fits; the rest fall back to the slow path.test()is already near-trivial per-call overhead (seebench/set-match.mjs) — small absolute savings.
N-API exposes a different set of levers that achieve the same boundary-shift intent without depending on Fast API. Tracked in the project queue under "Zero-copy JS↔C++ boundary at N-API rewrite time":
-
napi_create_external_buffer/napi_create_external_string_{latin1,utf16}— result strings/buffers that point into RE2-owned memory; no per-match allocation/copy. V8 strings created this way pin caller-owned memory until GC drops the reference. -
napi_get_typedarray_info/napi_get_buffer_info— zero-copy access to input bytes when the caller passes a Buffer/TypedArray. - Persistent property references — amortise V8 lookup costs across calls.
- Extending the existing same-string conversion cache — currently amortises UTF-16→UTF-8 within a call; backing match results with external strings would amortise across calls on the same source.
- V8 Fast API header
v8-fast-api-calls.h - Node internal-binding doc —
adding-v8-fast-api.md - Using V8 Fast API in Node.js core — yagiz.co (2023-01-13)
- v8-dev — Questions about the state of v8 fast API (April 2025)
- nodejs/node#55725 — "Missing v8-fast-api-calls.h header & FastApiTypedArray symbols" (closed not-planned)
- node-re2 discussions/#218 — the original optimization-opportunities thread that motivated this research
- V8 non-backtracking RegExp engine — related research on V8 internals relevant to node-re2's positioning
- RE2 lookbehinds fork assessment — related research on an RE2 fork
- Notes on building alternatives — broader thinking on the N-API rewrite and toolchain alternatives
Installing
Troubleshooting
- Problem: ABI mismatch in Electron
- Problem: build vs prod environments
- Problem: non-ASCII install path
Developers
Research and notes
- Notes on building alternatives
- Related projects
- FilteredRE2 evaluation
- RE2 lookbehinds fork assessment
- V8 Fast API assessment
- V8 non-backtracking RegExp engine
Project
Repository · README · prebuilds via install-artifact-from-github