Skip to content

ffi: add shared-buffer fast path for numeric and pointer signatures#62918

Open
bengl wants to merge 2 commits intonodejs:mainfrom
bengl:bengl/shared-buffer-ffi
Open

ffi: add shared-buffer fast path for numeric and pointer signatures#62918
bengl wants to merge 2 commits intonodejs:mainfrom
bengl:bengl/shared-buffer-ffi

Conversation

@bengl
Copy link
Copy Markdown
Member

@bengl bengl commented Apr 23, 2026

Adds an ArrayBuffer-based invocation path for FFI functions whose
signatures are composed entirely of numeric types (i8..i64, u8..u64,
f32, f64, bool, char) and/or pointer types. The JS wrapper packs
arguments directly into a per-function AB via primordial DataView
setters and the C++ invoker (InvokeFunctionSB) reads them without
going through V8's FunctionCallbackInfo. Results are returned the same
way.

Pointer arguments use runtime dispatch: BigInt, null, and undefined take
the fast path, while Buffer, ArrayBuffer, ArrayBufferView, and String
fall back transparently to the classic InvokeFunction path via a
stashed _invokeSlow function. Signatures containing
non-numeric/non-pointer types also bypass the fast path.

The fast path is disabled on big-endian platforms.

Callers do not opt in, and the fast path is transparent in every way
users should rely on. One observable change: function wrappers returned
by library.getFunction, library.getFunctions, and library.functions
now have .length equal to the declared parameter count rather than
0. Code that relied on the previous value will need to be updated.


This is largely based on #46905, which is largely based on https://github.com/bengl/sbffi.


Benchmark output:

                                       confidence improvement accuracy (*)   (**)  (***)
ffi/add-f64.js n=10000000                     ***     34.11 %       ±1.28% ±1.70% ±2.23%
ffi/add-i32.js n=10000000                     ***     32.46 %       ±0.75% ±1.01% ±1.32%
ffi/getpid.js n=10000000                       **      1.89 %       ±1.24% ±1.67% ±2.19%
ffi/many-args.js n=10000000                   ***     -7.10 %       ±0.52% ±0.69% ±0.89%
ffi/pointer-bigint.js n=10000000              ***     60.00 %       ±0.85% ±1.14% ±1.49%
ffi/sum-buffer.js n=1000000 size=1024         ***     20.46 %       ±1.16% ±1.55% ±2.04%
ffi/sum-buffer.js n=1000000 size=16384        ***      2.72 %       ±0.74% ±0.98% ±1.28%
ffi/sum-buffer.js n=1000000 size=64           ***     32.15 %       ±2.82% ±3.77% ±4.95%

@nodejs-github-bot
Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/performance

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Apr 23, 2026
Adds microbenchmarks covering the common FFI call shapes so future
changes to the invoker can be evaluated:

- add-i32.js: 2-arg integer
- add-f64.js: 2-arg float
- many-args.js: 6-arg integer
- pointer-bigint.js: 1-arg pointer (BigInt)
- sum-buffer.js: pointer + length (Buffer)

A `common.js` helper resolves the fixture-library path from
`test/ffi/fixture_library` without pulling in the test harness, and
throws a clear message if the fixture hasn't been built yet.

Also adds `sum_6_i32` to the fixture library for the many-args case.
@bengl bengl force-pushed the bengl/shared-buffer-ffi branch from 5dea1bf to 7b165f9 Compare April 24, 2026 02:48
@bengl bengl marked this pull request as ready for review April 24, 2026 02:49
Adds an ArrayBuffer-based invocation path for FFI functions whose
signatures are composed entirely of numeric types (i8..i64, u8..u64,
f32, f64, bool, char) and/or pointer types. The JS wrapper packs
arguments directly into a per-function AB via primordial DataView
setters and the C++ invoker (`InvokeFunctionSB`) reads them without
going through V8's `FunctionCallbackInfo`. Results are returned the same
way.

Pointer arguments use runtime dispatch: BigInt, null, and undefined take
the fast path, while Buffer, ArrayBuffer, ArrayBufferView, and String
fall back transparently to the classic `InvokeFunction` path via a
stashed `_invokeSlow` function. Signatures containing
non-numeric/non-pointer types also bypass the fast path.

The fast path is disabled on big-endian platforms.

Callers do not opt in, and the fast path is transparent in every way
users should rely on. One observable change: function wrappers returned
by `library.getFunction`, `library.getFunctions`, and `library.functions`
now have `.length` equal to the declared parameter count rather than
`0`. Code that relied on the previous value will need to be updated.
@bengl bengl force-pushed the bengl/shared-buffer-ffi branch from 7b165f9 to 676f0ae Compare April 24, 2026 03:04
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 24, 2026

Codecov Report

❌ Patch coverage is 80.89480% with 158 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.50%. Comparing base (a5b3d76) to head (676f0ae).

Files with missing lines Patch % Lines
lib/internal/ffi-shared-buffer.js 79.41% 124 Missing and 3 partials ⚠️
src/node_ffi.cc 82.73% 12 Missing and 12 partials ⚠️
src/ffi/types.cc 89.55% 1 Missing and 6 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #62918      +/-   ##
==========================================
- Coverage   89.61%   89.50%   -0.11%     
==========================================
  Files         706      707       +1     
  Lines      219218   219871     +653     
  Branches    42002    42070      +68     
==========================================
+ Hits       196446   196794     +348     
- Misses      14666    14936     +270     
- Partials     8106     8141      +35     
Files with missing lines Coverage Δ
lib/ffi.js 96.59% <100.00%> (+0.02%) ⬆️
src/node_ffi.h 78.57% <100.00%> (+5.84%) ⬆️
src/ffi/types.cc 48.68% <89.55%> (-11.47%) ⬇️
src/node_ffi.cc 69.98% <82.73%> (+1.18%) ⬆️
lib/internal/ffi-shared-buffer.js 79.41% <79.41%> (ø)

... and 55 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants