| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
CI finished, I see only two failures in Windows build configs which are network timeouts. I guess unrelated? |
Sorry, something went wrong.
|
Hasn't Trevor already changed this between JS and C++ like 3 times? 😂 |
Sorry, something went wrong.
|
@Fishrock123 Well, it's mostly in JS, but the inheritance from Uint8Array was done inefficiently. I just additionally removed CreateFromArrayBuffer in C++ as it's not needed when you have native subclass of Uint8Array which can be already instantiated with an ArrayBuffer as an argument (and do that faster). TL;DR: the biggest win here is unrelated to the C++ change, but rather to the ES6 native subclassing. |
Sorry, something went wrong.
|
@nodejs/buffer |
Sorry, something went wrong.
|
I like the changes very much. Also makes it more readable... I'll test around .slice since I conducted some experiments and found out that v8 is already really fast there. I am curious whether there is an regression now. |
Sorry, something went wrong.
Well, now Buffer.slice is very close to Uint8Array.subarray (I was comparing against it in my local benchmarks as a "theoretical maximum" which obviously can't be achieved in a wrapper, but can be very close (and it is now)). Please do let me know if I missed something / need to change before this can be merged. |
Sorry, something went wrong.
There was a problem hiding this comment.
Side note: this comment is irrelevant now, probably since dd67608, I overlooked it.
/cc @trevnorris
Sorry, something went wrong.
There was a problem hiding this comment.
Looks like it can be removed.
Sorry, something went wrong.
There was a problem hiding this comment.
Done.
Sorry, something went wrong.
There was a problem hiding this comment.
Looks like it has returned after a rebase =).
It's not critical, though, that could be removed later.
Sorry, something went wrong.
|
Not directly related, but now that I'm trying to submit changes from my Windows machine (previous one was from Mac), I've found one error: .eslintrc reports every line as invalid due to linebreak-style: [2, "unix"] in .eslintrc, while Git by default matches OS native endlines and checks out with CRLF (unless .gitattributes specifies custom eol for all text files, and it doesn't in this repo). What would be the best fix for this - a PR that removes .eslintrc rule against that or a PR that adds * text=auto eol=lf or [another option]? |
Sorry, something went wrong.
|
I think that discussion would best be taken to #6912 :) |
Sorry, something went wrong.
|
Oh cool, thanks! That's a new issue I haven't noticed yet :) |
Sorry, something went wrong.
|
Btw, can someone please explain what function SlowBuffer(length) {
if (+length != length)
length = 0;
is for / supposed to do? Just tried to wrap my head around it, and wasn't sure whether the additional semantics on top of simple typeof length !== 'number' were intended or accidental. |
Sorry, something went wrong.
|
There was some discussion on that in #2635… though I can’t seem to find the advantage of using +length != length, either. It behaves differently for inputs like false or '42', and probably not even in a wanted way. |
Sorry, something went wrong.
|
Note that it only exists as part of a deprecated API anyway. |
Sorry, something went wrong.
Exactly my thoughts.
That's true, just looks pretty weird when trying to read / understand the code. |
Sorry, something went wrong.
There was a problem hiding this comment.
This should preferably use Buffer.from instead of new Buffer
Sorry, something went wrong.
There was a problem hiding this comment.
Oh ok.
Sorry, something went wrong.
|
I’d maybe separate the <= to < change, along with its regresseion test, out into its own commit that can be landed separately too. LGTM either way. |
Sorry, something went wrong.
|
@RReverser Kinda… you don’t have to move that if you don’t want to, but keep in mind that the commit history ideally still makes sense for someone looking at it in a few years, without having the context of this PR in mind. Happens more often than you think. :) Also, it would be cool if you could re-format the commit message for that commit so that it adheres to the guidelines (i.e. it starts with buffer:, has an all-lowercase subject line, and is under < 72 columns) |
Sorry, something went wrong.
|
Hm. If this PR addresses the comment removal, leave it in its own commit. Yes it's minor, but if this needs to be reverted for some unforeseen reason don't want the comment coming back in. As far as the regression, I vote we leave that to its own PR (since it'll require it's own regression test, etc.). |
Sorry, something went wrong.
|
@trevnorris Want to go ahead and land this then? |
Sorry, something went wrong.
| if (size <= 0) | ||
| return createBuffer(size); | ||
| if (fill !== undefined) { | ||
| if (size > 0 && fill !== undefined) { |
There was a problem hiding this comment.
Does a separate check for 0 make sense here, i.e. size > 0 && fill !== undefined && fill !== 0?
Buffer.alloc(size, 0) is equivalent to Buffer.alloc(size), so just new FastBuffer(size) should work faster in that case.
Sorry, something went wrong.
There was a problem hiding this comment.
Hm, I think that would require benchmarking – createUnsafeBuffer() returns a slice from the pool, so I’d actually expect that to be faster than an extra typed array allocation.
Sorry, something went wrong.
There was a problem hiding this comment.
@addaleax It's not just createUnsafeBuffer, it's createUnsafeBuffer + fill(0).
I believe that has been discussed before, and allocation was proven to be faster — that's why simple Buffer.alloc(size) does not use the pool.
Sorry, something went wrong.
There was a problem hiding this comment.
@ChALkeR I know there’s the extra fill() in there. But if you say it’s faster, I believe that :)
Sorry, something went wrong.
There was a problem hiding this comment.
@addaleax Btw, nothing in this function uses slices from the pool. Perhaps it should?
Both new FastBuffer and createUnsafeBuffer just directly allocate a new instance.
Sorry, something went wrong.
There was a problem hiding this comment.
Oh, right. Maybe, but I’d leave that open for another PR, too, especially as it would introduce the subtle change that the return values of Buffer.alloc() would share their buffer property.
Sorry, something went wrong.
There was a problem hiding this comment.
i'm down for that change. The kernel can probably optimize calls to calloc() a hair better. Though not going to consider it a blocking change.
Sorry, something went wrong.
|
LGTM |
Sorry, something went wrong.
|
@addaleax Going ahead w/ these sounds good to me. |
Sorry, something went wrong.
|
Curious: would Buffer.alloc = function(size, fill, encoding) {
assertSize(size);
if (size > 0 && fill !== undefined && fill !== 0) {
if (typeof encoding !== 'string')
encoding = undefined;
return allocate(size).fill(fill, encoding);
}
return new FastBuffer(size);
};be faster for short buffers filled with some non-zero argument? There are two changes here: && fill !== 0 (for Buffer.alloc(size, 0) opt), and createUnsafeBuffer to allocate change to use the pool for short buffers. |
Sorry, something went wrong.
|
On a second thought, we can do that in a separate PR, those are independent changes. |
Sorry, something went wrong.
|
I’m going to land this later today if nobody beats me to it. |
Sorry, something went wrong.
Yes, thought about similar further optimizations, but they would be rather backward-incompatible and cases where they give any win are more rare, so decided not to change. |
Sorry, something went wrong.
Improves performance of allocating unsafe buffers, creating buffers from an existing ArrayBuffer and creating .slice(...) from existing Buffer by avoiding deoptimizing change of prototype after Uint8Array allocation in favor of ES6 native subclassing. This is done through an internal ES6 class that extends Uint8Array and is used for allocations, but the regular Buffer function is exposed, so calling Buffer(...) with or without `new` continues to work as usual and prototype chains are also preserved. Performance wins for .slice are +120% (2.2x), and, consequently, for unsafe allocations up to +95% (1.9x) for small buffers, and for safe allocations (zero-filled) up to +30% (1.3x). PR-URL: #6893 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com>
|
Landed in 5292a13. Thanks for the contribution and for your patience with us! |
Sorry, something went wrong.
|
@addaleax Thank you! That was quite a trip, but totally fine as for the first PR to the project :) |
Sorry, something went wrong.
No arguing about that. 😄 If you like, you can also do PRs for some of the issues that popped up as side notes in the discussion here. If not, you don’t have to, of course.
… if I’ve managed to get everything right. ;) |
Sorry, something went wrong.
|
@addaleax Also .alloc(size, fill) should probably use the pool since it fills manually either way — #6893 (comment). |
Sorry, something went wrong.
|
@ChALkeR It was deliberate to not have Buffer.alloc() use the pool, since it was introduced to force the user to safety, and allocating from the pool allows others to read your memory. For example: var b;
while ((b = Buffer.allocUnsafe(1)).byteOffset > 0);
Buffer.from(b.buffer).fill(0);
setTimeout(() => {
// See what else has been written to the buffer since
console.log(b);
}, 3000);Can collect more information by messing with Buffer.poolSize. The argument is that allowing those allocations to come from the pool undermines the secure aspect they're focused on. |
Sorry, something went wrong.
|
@trevnorris Ah, understood. I personally don't see how that is a problem, because .buffer properties are accessible only locally (i.e. not saved to the db, not transfered over network, etc), and we don't (and can't) gurantee any safety in presence of local malicious code. But ok, let's keep it that way if there are concerns about that. Perhaps that should be documented as a small one-line comment in the source code? |
Sorry, something went wrong.
|
@ChALkeR That decision was simply my call when it was first implemented to make sure the PR would avoid additional scrutiny. If everyone's alright with using the pool then I won't stand in the way. |
Sorry, something went wrong.
|
@trevnorris On a second though, I think that you are correct here and that we should keep that as it is now. There could be various code errors on user side which could potentially cause issues if the code somehow uses the .buffer property. Also, the current behaviour is documented, and changing that would be a semver-major. So let's not change that =). |
Sorry, something went wrong.
|
This depends on #7082 and #7093, both of which have been marked dont-land-on-v6.x. @RReverser interested in opening a backport PR against the v6.x branch? |
Sorry, something went wrong.
|
#7176 (comment) same question here |
Sorry, something went wrong.
Improves performance of allocating unsafe buffers, creating buffers from an existing ArrayBuffer and creating .slice(...) from existing Buffer by avoiding deoptimizing change of prototype after Uint8Array allocation in favor of ES6 native subclassing. This is done through an internal ES6 class that extends Uint8Array and is used for allocations, but the regular Buffer function is exposed, so calling Buffer(...) with or without `new` continues to work as usual and prototype chains are also preserved. Performance wins for .slice are +120% (2.2x), and, consequently, for unsafe allocations up to +95% (1.9x) for small buffers, and for safe allocations (zero-filled) up to +30% (1.3x). PR-URL: #7349 Ref: #6893 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Trevor Norris <trev.norris@gmail.com>
| Back | FazBrowse Home | New Git URL |
Checklist
Affected core subsystem(s)
buffer
Description of change
Improves performance of allocating unsafe buffers, creating buffers from
an existing ArrayBuffer and creating .slice(...) from existing Buffer by
avoiding deoptimizing change of prototype after Uint8Array allocation
in favor of ES6 native subclassing.
This is done through an internal ES6 class that extends Uint8Array and
is used for allocations, but the regular Buffer function is exposed, so
calling Buffer(...) with or without new continues to work as usual
and prototype chains are also preserved.
Performance wins for .slice are +120% (2.2x), and, consequently, for
unsafe allocations up to +95% (1.9x) for small buffers, and for safe
allocations (zero-filled) up to +30% (1.3x).