Skip to content

buffer: support aligned allocations - #2

Open
ronag wants to merge 1 commit into
mainfrom
buffer-aligned-alloc
Open

buffer: support aligned allocations#2
ronag wants to merge 1 commit into
mainfrom
buffer-aligned-alloc

Conversation

@ronag

@ronag ronag commented Aug 3, 2026

Copy link
Copy Markdown
Owner

Adds an optional alignment argument to Buffer.allocUnsafe() and
Buffer.allocUnsafeSlow(), guaranteeing that the memory backing the returned
buffer starts at an address that is a multiple of alignment.

Why

Some OS interfaces refuse to work with unaligned memory. The motivating case is
unbuffered ("direct") file I/O: a read or write on a descriptor opened with
O_DIRECT fails with EINVAL unless the buffer address is a multiple of the
device's logical block size. There was previously no way to get such a buffer
from JS — the address of a backing store can neither be observed nor chosen.
Alignment is also useful purely for performance, e.g. keeping a hot buffer from
straddling one more cache line than its size requires.

How

V8 does not allow picking the address of a backing store, so alignment is
achieved by over-allocating alignment - 1 bytes and positioning the buffer at
the first suitably aligned byte within them. A new arrayBufferAlignedOffset()
binding computes that offset.

It returns an offset rather than a finished Buffer for two reasons: external
backing stores are rejected outright when the V8 sandbox is enabled, and
Buffer::New() is not usable during buffer.js evaluation — the buffer
prototype is only wired up after require('buffer') returns, which the pool
creation runs before.

The Buffer.allocUnsafe() pool is now cache-line aligned itself, so pooled
allocations can satisfy any alignment up to 64 bytes by padding their offset
into the pool instead of allocating separately. poolOffset is therefore now
tracked relative to a new poolBase.

Status

  • Not built or tested. eslint (lib + doc samples) and cpplint pass;
    lint-md reports only the expected pr-url: REPLACEME warnings.
  • Doc changes: entries still need the real PR number.

@ronag
ronag force-pushed the buffer-aligned-alloc branch 3 times, most recently from bb15ba4 to f35f20d Compare August 4, 2026 14:56
Add an optional `alignment` argument to `Buffer.allocUnsafe()` and
`Buffer.allocUnsafeSlow()`, which guarantees that the memory backing the
returned buffer starts at an address that is a multiple of `alignment`.

Some operating system interfaces refuse to work with unaligned memory.
The motivating case is unbuffered ("direct") file I/O: a read or write
on a descriptor opened with `O_DIRECT` fails with `EINVAL` unless the
buffer address is a multiple of the logical block size of the underlying
device. Until now there was no way to obtain such a buffer from JS,
since the address of a backing store can neither be observed nor chosen.
Alignment is also worth having purely for performance, for instance to
keep a hot buffer from straddling one more cache line than its size
requires.

V8 does not allow picking the address of a backing store, so alignment
is instead achieved by over-allocating `alignment - 1` bytes and
positioning the buffer at the first suitably aligned byte within them.
The new `arrayBufferAlignedOffset()` binding computes that offset.

Addresses are not stable across snapshot serialization, so the binding
reports no padding while a snapshot is being built. Otherwise the
snapshot would capture where this particular process happened to
allocate and stop being reproducible. Buffers restored from a snapshot
are consequently not aligned; the pool works around this by recreating
itself in a deserialize callback.

The `Buffer.allocUnsafe()` pool is now aligned to a cache line itself,
which lets pooled allocations satisfy any alignment up to 64 bytes by
padding their offset into the pool rather than allocating separately.

Assisted-by: Claude/Opus 5
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Robert Nagy <ronagy@icloud.com>
@ronag
ronag force-pushed the buffer-aligned-alloc branch from f35f20d to 542b4f1 Compare August 4, 2026 15:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant