Skip to content

buffer: optimize Buffer.prototype.copy#63828

Open
ronag wants to merge 2 commits into
nodejs:mainfrom
ronag:buffer-faster-copy
Open

buffer: optimize Buffer.prototype.copy#63828
ronag wants to merge 2 commits into
nodejs:mainfrom
ronag:buffer-faster-copy

Conversation

@ronag

@ronag ronag commented Jun 10, 2026

Copy link
Copy Markdown
Member

Route the native backing of Buffer.prototype.copy (CopyImpl, the _copy binding) through V8's new v8::ArrayBuffer::CopyArrayBufferBytes API instead of materializing an ArrayBufferViewContents and doing a manual memmove. This speeds up partial copies (sourceStart > 0). All copies now go through this binding: the previous %TypedArray%.prototype.set fast-path for whole-buffer copies is dropped, since it would throw on a detached or immutable target rather than report a 0-byte no-op, and the benchmarks below show the native path is comparable for that case.

V8 clamps the byte range and handles detached and immutable buffers. When both sides are backed by a SharedArrayBuffer the relaxed-atomic overload is used, which honors the SharedArrayBuffer memory model. Mixed copies (one SharedArrayBuffer, one ArrayBuffer) use the regular overload.

copy() now reports the number of bytes actually copied, as returned by V8: 0 when the target is backed by a detached or immutable ArrayBuffer. The copy is then a no-op rather than a write to read-only memory.

buffer-copy.js vs node v26.3.0 (x64, 30 runs):
partial=false bytes=1024: +7.91% ()
partial=false bytes=128: +0.17%
partial=false bytes=8: -0.89%
partial=true bytes=1024: +22.33% (
)
partial=true bytes=128: +19.58% ()
partial=true bytes=8: +18.70% (
)

The V8 API is carried as a floating patch cherry-picked from:

https://chromium-review.googlesource.com/c/v8/v8/+/7735151

That CL is authored by a Node.js collaborator (Ben Noordhuis) and is expected to land upstream, so the floating patch should be easy to maintain: it touches nothing but the public ArrayBuffer API and can be dropped wholesale on the next V8 upgrade that includes it. v8_embedder_string is bumped to -node.21 accordingly.

This supersedes the prototype in
#62491, which added a bespoke ArrayBufferView::FastCopy instead of using the upstream-friendly CopyArrayBufferBytes API.

Adds SharedArrayBuffer and immutable-ArrayBuffer coverage to the buffer copy tests.

Assisted-by: Claude Opus 4.8 (1M context) noreply@anthropic.com

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/gyp
  • @nodejs/security-wg
  • @nodejs/v8-update

@nodejs-github-bot nodejs-github-bot added 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 Jun 10, 2026
@ronag ronag requested review from jasnell and mcollina June 10, 2026 06:51
@ronag

ronag commented Jun 10, 2026

Copy link
Copy Markdown
Member Author

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates Node’s Buffer.prototype.copy() implementation to use V8’s new ArrayBuffer::CopyArrayBufferBytes / SharedArrayBuffer::CopyArrayBufferBytes APIs, improving performance for partial copies and ensuring detached/immutable targets become no-op copies that report 0 bytes copied.

Changes:

  • Route the native _copy binding through V8’s new CopyArrayBufferBytes API (including relaxed-atomic handling when both sides are SharedArrayBuffer-backed).
  • Update JS-side copy() to return the actual number of bytes copied (as reported by the native binding), including 0 for immutable/detached targets.
  • Add test coverage for SharedArrayBuffer copies and immutable ArrayBuffer targets, plus V8 cctests and API header/docs updates.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
test/parallel/test-buffer-copy.js Adds SharedArrayBuffer copy coverage (including overlap + byteOffset cases).
test/parallel/test-buffer-copy-immutable.js Adds coverage for copying into immutable ArrayBuffer-backed targets (expects 0 bytes copied).
src/node_buffer.cc Reworks native copy to call V8 CopyArrayBufferBytes and return actual copied byte count.
lib/buffer.js Simplifies JS copy path to rely on native _copy return value for bytes-copied semantics.
deps/v8/test/cctest/test-api-array-buffer.cc Adds V8 cctests for the new CopyArrayBufferBytes APIs (detached + immutable cases).
deps/v8/src/api/api.cc Implements ArrayBuffer::CopyArrayBufferBytes and SharedArrayBuffer::CopyArrayBufferBytes.
deps/v8/include/v8-array-buffer.h Exposes the new public V8 API methods and documents their behavior.
common.gypi Bumps v8_embedder_string to reflect the added floating V8 patch.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread test/parallel/test-buffer-copy.js Outdated
Comment thread deps/v8/include/v8-array-buffer.h
Comment thread lib/buffer.js
Comment thread test/parallel/test-buffer-copy-immutable.js Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.

@marco-ippolito

Copy link
Copy Markdown
Member

can you split the v8 patch commit and the implementation commit?

Add v8::ArrayBuffer::CopyArrayBufferBytes and
v8::SharedArrayBuffer::CopyArrayBufferBytes, which copy a byte range
from one backing store to another. V8 clamps the byte range and handles
detached and immutable buffers: nothing is copied when the source is
detached or the target is detached or immutable, and the number of bytes
actually copied is returned. The SharedArrayBuffer overload uses a
relaxed-atomic memmove that honors the SharedArrayBuffer memory model.

Carried as a floating patch cherry-picked from:

    https://chromium-review.googlesource.com/c/v8/v8/+/7735151

That CL is authored by a Node.js collaborator (Ben Noordhuis) and is
expected to land upstream, so the floating patch should be easy to
maintain: it touches nothing but the public ArrayBuffer API and can be
dropped wholesale on the next V8 upgrade that includes it.
v8_embedder_string is bumped to -node.21 accordingly.

Refs: https://chromium-review.googlesource.com/c/v8/v8/+/7735151
Signed-off-by: Robert Nagy <ronagy@icloud.com>
Comment thread src/node_buffer.cc Outdated
Route the native backing of Buffer.prototype.copy (CopyImpl, the `_copy`
binding) through V8's new v8::ArrayBuffer::CopyArrayBufferBytes API
(added in the preceding commit) instead of materializing an
ArrayBufferViewContents and doing a manual memmove. This speeds up
partial copies (sourceStart > 0). All copies now go through this
binding: the previous %TypedArray%.prototype.set fast-path for
whole-buffer copies is dropped, since it would throw on a detached or
immutable target rather than report a 0-byte no-op, and the benchmarks
below show the native path is comparable for that case.

When both sides are backed by a SharedArrayBuffer the relaxed-atomic
overload is used, which honors the SharedArrayBuffer memory model.
Mixed copies (one SharedArrayBuffer, one ArrayBuffer) use the regular
overload.

copy() now reports the number of bytes actually copied, as returned by
V8: 0 when the target is backed by a detached or immutable ArrayBuffer.
The copy is then a no-op rather than a write to read-only memory.

The native binding now plumbs byte offsets and the copied-byte count
through as size_t, passed across the fast/slow API boundary as doubles
(exact for integer values below 2^53), so copies that cross the 4 GiB
boundary are no longer truncated to 32 bits.

buffer-copy.js vs node v26.3.0 (x64, 30 runs):
  partial=false bytes=1024:  +7.91%  (***)
  partial=false bytes=128:   +0.17%
  partial=false bytes=8:     -0.89%
  partial=true  bytes=1024: +22.33%  (***)
  partial=true  bytes=128:  +19.58%  (***)
  partial=true  bytes=8:    +18.70%  (***)

This supersedes the prototype in
nodejs#62491, which added a bespoke
ArrayBufferView::FastCopy instead of using the upstream-friendly
CopyArrayBufferBytes API.

Adds SharedArrayBuffer and immutable-ArrayBuffer coverage to the buffer
copy tests, plus a pummel regression test for copies larger than 2^32
bytes.

Refs: nodejs#55422
Signed-off-by: Robert Nagy <ronagy@icloud.com>
Assisted-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bnoordhuis

Copy link
Copy Markdown
Member

That CL is authored by a Node.js collaborator (Ben Noordhuis) and is expected to land upstream

For posterity, said CL was merged upstream earlier today.

@ronag ronag added request-ci Add this label to start a Jenkins CI on a PR. author ready PRs that have at least one approval, no pending requests for changes, and a CI started. labels Jun 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author ready PRs that have at least one approval, no pending requests for changes, and a CI started. lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. request-ci Add this label to start a Jenkins CI on a PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants