-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
buffer: move checkFloat from lib into src #3763
Conversation
Environment* env = Environment::GetCurrent(args); | ||
|
||
bool noAssert = args[3]->BooleanValue(); | ||
if (!noAssert) { |
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 avoid double negative stuff here, if you simply name it as should_assert
or something like that.
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.
It is named this way to maintain consistency with the existing JS api.
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.
@matthewloring I think the convention in C++ is to use snake case for local variables and I am more interested in something like this
bool should_assert = !args[3]->BooleanValue();
...
if (should_assert) {
...
}
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.
It is convention to use snake_case in native code, and since I've been bitten in the butt by it before (in buffer code actually) I'll have to agree with @thefourtheye's observation of avoiding double negative is a better option.
Also, we'll need to do some benchmarking on BooleanValue()
. Last time I used it (way way back in v0.10) the performance was bad enough that we actually prefixed the check with IsUndefined()
. So may look something like this:
bool should_assert = !args[3]->IsUndefined() && !args[3]->BooleanValue();
Think that's right, but it's getting late.
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 believe the condition should be args[3]->IsUndefined() || !args[3]->BooleanValue()
. Alternatively, we could use !!
on the JS side to ensure we only get booleans at this point the same way the other arguments are type coerced value = +value; offset = offset >>> 0;
. Do you have a preference between these?
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.
In the mean time, I've fixed the case and negation issues here.
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.
These v8::Value::XValue
methods are scheduled for deprecation in favor of the Maybe
versions.
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've updated the value accesses in this function to use the new version. It looks like the non-Maybe
versions are still used elsewhere throughout the file.
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.
@matthewloring From my distant memory it was indeed faster to coerce noAssert
in JS. Then you can do a simple IsTrue()
check, and drop the other two.
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.
@trevnorris I've moved the boolean coercion into JS.
CHECK_LE(offset + sizeof(T), ts_obj_length); | ||
if (!noAssert) { | ||
CHECK_NOT_OOB(offset + sizeof(T) <= ts_obj_length); | ||
} |
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.
Drop the CHECK_LE below the conditional check. We still want to abort to prevent writing to memory bits we don't have control over.
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.
NM this comment. Logic will change w/ another upcoming PR.
@matthewloring Awesome job. Thanks much for taking care of this. Has been one of those changes in my backlog that never taken the time to get to. Since this is all about micro-optimization (we'll only be shaving off 20ns or so here) there may be some experimentation we'll need to do |
@matthewloring There's going to be a collision with #3767. It only touches the location where you removed the EDIT: Ref'd issue was this one. Has been fixed. |
@trevnorris It should be an easy merge conflict to resolve. You're welcome to land #3767. |
checkFloat(this, val, offset, 4); | ||
binding.writeFloatBE(this, val, offset); | ||
noAssert = !!noAssert; | ||
binding.writeFloatBE(this, val, offset, noAssert); |
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.
sorry, minor nit: do the boolean coercion in the call itself. e.g. writeFloatBE(this, val, offset, !!noAssert);
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.
Inlined.
Looks great. As soon as I can land the previously mentioned patch, get this rebased and CI is happy we'll be good to land. LGTM. |
T val = args[1]->NumberValue(); | ||
uint32_t offset = args[2]->Uint32Value(); | ||
CHECK_LE(offset + sizeof(T), ts_obj_length); | ||
T val = args[1]->NumberValue(env->context()).FromMaybe(0); |
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.
Why does this function take the T
parameter? T
is used as the return type for v8::Value::NumberValue::FromMaybe
, which returns a double
.
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.
val
may either be a double
or float
.
LGTM |
@trevnorris ... as an optimization, I'm inclined not to flag this for LTS but would like your input on that. |
@jasnell Sounds reasonable. I think this is low risk, but also not critical. |
Looks like this may be integer overflow on 32-bit architectures? I can verify and put together a fix. |
@matthewloring ping me when you have a fix, or have a question, and will run CI again. |
@trevnorris I've added the lower bounds check which I believe to fix the problem but do not have a centos 32 vm to reproduce the initial failure on. Are there existing overflow prevention checks elsewhere inside buffer that I should emulate or does the added check |
The |
@matthewloring Just landed the other conflicting PR. Mind rebasing on latest master? The other PR alerted me to the specific wording in the current documentation:
So I believe if |
@trevnorris I've rebased and reinserted the |
Failures (mostly crypto timeouts) should be unrelated flakes. |
@matthewloring We'll need to do some investigation. The call is slower than it is now. This is the fun part. :) I'll update if I find any details. Here's a simple script I'm using: 'use strict';
const ITER = 1e7;
var b = new Buffer(8);
var t = process.hrtime();
for (var i = 0; i < ITER; i++) {
b.writeDoubleLE(0, 0, true);
}
t = process.hrtime(t);
console.log(((t[0] * 1e9 + t[1]) / ITER).toFixed(1) + ' ns/op'); (note: this is fine b/c the call into native isn't affected by the optimizing compiler like JS would be) |
Hmm, I can look into this as well. |
It looks like at least part of the slow down is caused by the new maybe versions of NumberValue and Uint32Value. I see ~53 ns/op with the maybe versions compared to ~46 ns/op with the old versions of NumberValue and Uint32Value and ~44 ns/op with the checks remaining in JS (at master). |
@matthewloring Here's a diff gaining back the lost performance: https://gist.github.com/trevnorris/3bce2822893a98e9d971 Sorry, this is where writing performant code makes things look worse. |
The new Maybe versions definitely aren't helping. Also getting the values out (e.g. |
The type and range checks performed by this function can be done more efficiently in native code.
That change brings script down to ~42 for me. I've applied your change. |
Only failure is a child process fork connection reset, seems good to me. |
Thanks much! Landed in 22478d3. |
The type and range checks performed by this function can be done more efficiently in native code. PR-URL: #3763 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
The type and range checks performed by this function can be done more efficiently in native code. PR-URL: #3763 Reviewed-By: Trevor Norris <trev.norris@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
Marking with |
The type and range checks performed by this function can be done more
efficiently in native code.
/cc @trevnorris