-
Notifications
You must be signed in to change notification settings - Fork 30.7k
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
src: use DataView for B.{read,write}{Float,Double} #2897
Conversation
I have to say I'm not really a fan of how this adds a new property to the Buffer prototype. |
Results of buffer benchmarks on current master before and after applying this patch: https://gist.github.com/targos/438a9cc9dc48fa178729 |
cc @trevnorris |
@bnoordhuis performance or api burden? Because I did not plan to document this property and I can rename to make it more clear. @targos My patch can't be only difference in your benchmark. This patch could only have a big impact for float and double related tests or insignificant slow down for example buffer-creation but cannot improve buffer-iterate. |
@skomski you are right, I ran the |
lib/buffer.js
Outdated
@@ -295,6 +295,17 @@ function byteLength(string, encoding) { | |||
Buffer.byteLength = byteLength; | |||
|
|||
|
|||
Object.defineProperty(Buffer.prototype, 'dataView', { | |||
get: function() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be configurable and probably enumerable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about this:
Object.defineProperty(Buffer.prototype, 'dataView', {
get: function() {
return this.dataView = new DataView(this.buffer, this.byteOffset, this.byteLength);
},
configurable: true,
enumerable: true
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can't set something that only has a getter. The current body of the getter is fine.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, my bad. So the body should call Object.defineProperty(this, 'dataView', { value: ... });
I don't like the fact that we need two properties (dataView and _dataView) for one.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm with @targos here. We do that some other places too.
I like adding a dataView property to the prototype, from an API perspective. But it should be documented. If it's not going to be documented then just use the _dataView property directly. |
Let me investigate why this is faster. The v8 API should be comparable in terms of performance. |
There are several extra checks that don't need to be done in the current implementation. A quick refactor removed the performance difference between the two. This would be the better approach. @skomski If you'd like to take this on, I can point you at which checks need to be changed/removed. Otherwise I can throw up a PR. Thanks for investigating the performance here. In the last refactor I went overly paranoid w/ checking to make sure nothing slipped through the cracks. |
I think there's independent value in (a) adding an easy dataView accessor; (b) removing all that C++ code in favor of using the stuff V8 already has. So this still seems like a good change to me... |
|
I don't believe it's a good idea to mutate the object (i.e. add property after instantiation). Which would in turn mutate the ObjectMap. Everything else in Buffer is either attached to the prototype or is statically assigned. In terms of performance, difference between the two is within the margin of error. Our implementation is slightly faster if In terms of compatibility difference, The first can be addressed by setting Setting object property after instantiation is my concern. Can anyone think of a way to get around it? |
4fa0061
to
145feeb
Compare
@trevnorris Pushed an update that simply defines the property |
@skomski Issue is as I mentioned:
Any Buffers created from C++ won't have this property set. So we'll then have two object maps being passed around. Solutions would be to also set the property in C++ (though I have reservations on the performance impact that would have, since JS operations in native are slow) or... Not sure. Have looked for a way to do this without setting a property on the object, but haven't found one. If it wasn't for needing to store a value on the instantiated Buffer (primarily because the operation needs to be performed in C++ and JS) I'd be +1 for this change. And if someone can find a way I think that would be great. Until then I'm inclined to say the better approach is to remove the overzealous checks. Which would still give comparable performance. |
Example script to demonstrate my issue: 'use strict';
let b = new Buffer(16);
for (let i = 0; i < 1e4; i++)
b.write('hi all');
b.readFloatLE(0);
b.write('hi all'); Running this script currently:
Running script with this PR (before the recent change of placing the
Altering the map will cause a deoptimization. Now, if we could simply immediately set it in JS after creating the |
Adds lazy-initialized dataView property to Buffer Removes C++ functions: ReadFloat, WriteFloat, ReadDouble, WriteDouble About 37,5% faster
145feeb
to
576af3b
Compare
If you wanted to avoid mutating the object, add a var dataViewMap = new WeakMap()
Object.defineProperty(Buffer.prototype, 'dataView', {
enumerable: true,
get: function() {
if (!dataViewMap.has(this)) {
var dv = new DataView(this.buffer, this.byteOffset, this.byteLength);
dataViewMap.set(this, dv);
return dv;
}
return dataViewMap.get(this);
}
}); Unsure what affect this would have on the perf of backing the buffer methods with dataview, but it should solve the mutated object problem. |
Pending #3080 the time gap between implementations will be around 5ns. There are additional optimizations to do afterwards, but will wait until the PR lands. |
What's the status on this one? @trevnorris any reason to keep this open? |
I haven't benchmarked recently, but last I checked this was within 10-20 ns of our implementation if there are enough writes to discount the time it takes to create the |
Noted. @skomski ... is this still something you wish to pursue? |
7da4fd4
to
c7066fb
Compare
c133999
to
83c7a88
Compare
Closing given the lack of progress |
Adds lazy-initialized dataView property to Buffer
Removes C++ functions: ReadFloat, WriteFloat, ReadDouble, WriteDouble
About 37,5% faster