Skip to content

buffer: avoid unnecessary copy in Buffer.concat for single FastBuffer #58266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed

Conversation

mertcanaltin
Copy link
Member

@mertcanaltin mertcanaltin commented May 10, 2025

If there's only one FastBuffer and its length matches the total length, we don't need to manually allocate and copy — we can just return Buffer.from(buf) to keep the semantics (new buffer) but avoid extra work.

my local result:

                                                                            confidence improvement accuracy (*)   (**)  (***)
buffers/buffer-concat-fill.js n=800000 extraSize=1                                         -0.37 %       ±0.78% ±1.04% ±1.36%
buffers/buffer-concat-fill.js n=800000 extraSize=1024                                       0.22 %       ±1.08% ±1.44% ±1.89%
buffers/buffer-concat-fill.js n=800000 extraSize=256                                       -0.88 %       ±1.40% ±1.87% ±2.45%
buffers/buffer-concat.js n=800000 withTotalLength=0 pieceSize=1 pieces=16                  -0.30 %       ±0.52% ±0.69% ±0.89%
buffers/buffer-concat.js n=800000 withTotalLength=0 pieceSize=1 pieces=4                    0.11 %       ±0.66% ±0.88% ±1.14%
buffers/buffer-concat.js n=800000 withTotalLength=0 pieceSize=16 pieces=16                 -0.18 %       ±1.44% ±1.92% ±2.50%
buffers/buffer-concat.js n=800000 withTotalLength=0 pieceSize=16 pieces=4                  -0.30 %       ±0.67% ±0.89% ±1.16%
buffers/buffer-concat.js n=800000 withTotalLength=0 pieceSize=256 pieces=16                -0.38 %       ±0.54% ±0.72% ±0.94%
buffers/buffer-concat.js n=800000 withTotalLength=0 pieceSize=256 pieces=4                 -0.44 %       ±0.65% ±0.87% ±1.13%
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=1 pieces=16           **      1.02 %       ±0.62% ±0.82% ±1.07%
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=1 pieces=4             *     -0.83 %       ±0.69% ±0.92% ±1.19%
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=16 pieces=16                  0.08 %       ±1.18% ±1.58% ±2.07%
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=16 pieces=4                  -0.25 %       ±0.67% ±0.90% ±1.17%
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=256 pieces=16                -0.19 %       ±0.56% ±0.74% ±0.97%
buffers/buffer-concat.js n=800000 withTotalLength=1 pieceSize=256 pieces=4                 -0.25 %       ±0.56% ±0.75% ±0.97%

Be aware that when doing many comparisons the risk of a false-positive result increases.
In this case, there are 15 comparisons, you can thus expect the following amount of false-positive results:
  0.75 false positives, when considering a   5% risk acceptance (*, **, ***),
  0.15 false positives, when considering a   1% risk acceptance (**, ***),
  0.01 false positives, when considering a 0.1% risk acceptance (***)

@nodejs-github-bot nodejs-github-bot added buffer Issues and PRs related to the buffer subsystem. needs-ci PRs that need a full CI run. labels May 10, 2025
Copy link

codecov bot commented May 10, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 90.18%. Comparing base (264cad7) to head (62de85d).
Report is 25 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main   #58266   +/-   ##
=======================================
  Coverage   90.18%   90.18%           
=======================================
  Files         629      629           
  Lines      186657   186640   -17     
  Branches    36658    36669   +11     
=======================================
- Hits       168328   168324    -4     
- Misses      11126    11128    +2     
+ Partials     7203     7188   -15     
Files with missing lines Coverage Δ
lib/buffer.js 100.00% <100.00%> (ø)

... and 38 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.

list[0] instanceof FastBuffer &&
list[0].length === length
) {
return Buffer.from(list[0]);
Copy link
Member

Choose a reason for hiding this comment

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

If list[0] is a TypedArray (like another Buffer) then this still copies.

Copy link
Member Author

Choose a reason for hiding this comment

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

I’m wondering if it makes sense to return list[0] directly. I understand this avoids copying, so I guess I should update the test accordingly?
https://github.com/nodejs/node/blob/main/test/parallel/test-buffer-concat.js#L43

Copy link
Member

Choose a reason for hiding this comment

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

Buffer.concat should always return a new buffer object:

node/doc/api/buffer.md

Lines 1033 to 1034 in 91d2400

Returns a new `Buffer` which is the result of concatenating all the `Buffer`
instances in the `list` together.

Copy link
Member Author

Choose a reason for hiding this comment

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

As I understand it, at the end of the day we should always create a buffer.

Copy link
Member Author

Choose a reason for hiding this comment

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

I will try an optimization in the inner regions, I am now closing this pr, thank you very much for your comments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
buffer Issues and PRs related to the buffer subsystem. needs-ci PRs that need a full CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants