-
Notifications
You must be signed in to change notification settings - Fork 30.2k
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
Optimize event handling with large arguments #9643
Conversation
In V8, it is faster to create an array by ```[]``` instead of ``` new Array()'''.
Is it still faster taking into consideration that the current code creates an appropriately sized array from the start? |
I'm a bit skeptical about this, can you provide a benchmark results comparison using benchmark/compare.R? |
@cjihrig @mscdex |
-1 if without benchmark result. |
I ran the Command I ran (after compiling the master branch and moving it to /var/tmp, then compiling the master branch with this patch applied and moving it to a different name in /var/tmp):
Command I then ran to process the data:
Results:
|
Yeah I don't see how calling |
ALTHOUGH...I changed the benchmark file as described by @simonkcleung in #9643 (comment) and the improvement if I do that is downright astonishing:
|
@Trott are you sure you changed that right? |
@Fishrock123 Yes, I'm pretty certain, but if you or someone else (@mscdex?) want to see if you can or can't replicate my results, that would be great. |
I also get a similar result:
It still feels terribly wrong to me... |
Can you post the diff? I'd like to give it a try. |
You mean this diff ? diff --git a/benchmark/events/ee-emit-multi-args.js b/benchmark/events/ee-emit-multi-args.js
index b423c21..0ca3026 100644
--- a/benchmark/events/ee-emit-multi-args.js
+++ b/benchmark/events/ee-emit-multi-args.js
@@ -14,7 +14,7 @@ function main(conf) {
bench.start();
for (var i = 0; i < n; i += 1) {
- ee.emit('dummy', 5, true);
+ ee.emit('dummy', 1, 2, 3, 4, 5, 6);
}
bench.end(n);
} |
So I profiled a run of this modified benchmark with current
This doesn't happen when this PR is applied (Runtime_CreateListFromArrayLike doesn't appear in the profile). |
Ok so the regression happened between node v5.x and v6.0.0, which is V8 v4.6 and v5.1 respectively. I've also found out that the performance regression actually has nothing to do with either the value types being assigned or the act of assigning values to the array in general, but it seems to be the array allocation causing the slowdown. If I remove the array assignment loop completely and just do:
So it's not related to explicit constructor use or the supplying of a length argument to the constructor, but it's whenever a positive, non-zero length is passed to the constructor. Without bisecting, there is one particular V8 commit that was landed during V8 4.9 where array construction logic was changed (and these changes still exist in master) that might be a good candidate... |
cc @bmeurer ? |
W/o looking further into the benchmark, it seems that there's some If you do
that creates a non-holey array (i.e. V8 knows that there are no holes in the elements and thus we don't need to fall back to lookups on prototypes). However
creates a holey array from the get go (thanks to EcmaScripts wonderful "length" property magic). So if you now use such an array with There's an open issue to extend this fast case at some point to also cover a bunch of holey array cases, but so far we didn't get back to that. |
Here is a simpler repro, it uses a constant length value (which I think V8 typically optimizes for?) but it still seems to exhibit the same slowdown: function foo() {
return 1 + 1;
}
var n = 2e6;
console.time('fn.apply');
for (var i = 0; i < n; i += 1) {
//var args = new Array(6);
//args[0] = args[1] = args[2] = args[3] = args[4] = args[5] = 1;
var args = [];
args.push(1);
args.push(1);
args.push(1);
args.push(1);
args.push(1);
args.push(1);
foo.apply(this, args);
}
console.timeEnd('fn.apply'); Replace the
EDIT: oops, didn't see the responses above before I posted this.... |
Yap, so it's the missing fast path for holey arrays in |
This is related to https://bugs.chromium.org/p/v8/issues/detail?id=4826, although that particular one is about the double arrays. |
Yes, that's indeed what happens.
|
I'll see if I can cook a quickfix for the FAST_HOLEY_SMI_ELEMENTS and FAST_HOLEY_ELEMENTS cases. |
Even var args = [];
args[0] = args[1] = args[2] = args[3] = args[4] = args[5] = 1;
fn.apply(this, args); is just as slow FWIW. |
Sure, you store to args[5] first, meaning you turn args into a holey array with holes at elements 0,...,4. |
Ah oops you're right :-) That's what I get for trying to make a one-liner... Reversing the indices is fast though as expected now. |
I have a fix here (intel only): https://codereview.chromium.org/2510043004 |
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 -1 on this. Using the preallocated array is preferable when possible.
As @bmeurer pointed out, using a holy array in |
I think we're still waiting for the S390 and PPC port of that V8 fix /cc @jasnell |
@jbajwa Thanks. So with your port, we have Intel, ARM, MIPS and PPC/s390 ports for the regression. @jasnell Pre-allocating an array is not generally beneficial, depending on how the array is used. For example, load/store access to non-holey array is generally faster. So if you can create an array by pushing elements to it, that can be performance-win if the potential overhead from copying during setup is not dominating. |
Any word on if/when the relevant upstream V8 changes will be backported to V8 5.4 (assuming V8 5.5 won't make it into node v7)? |
@mscdex Not sure if that’s even a partial answer to your question, but according to #9730 (comment) V8 5.4 is not maintained anymore, which I guess means that we’d have to take care of any backports ourselves? |
@addaleax If that's the case, I thought there was still supposed to be some sort of special coordination going on to get them backported on V8's side so we wouldn't have to float patches ourselves? Or is that only for V8 versions used by node LTS releases? |
Sorry, I don’t really know what our current process for these things is. @nodejs/v8 probably knows? |
As far as I know we only backmerge fixes for Node LTS versions (and Chrome versions in the wild). @fhinkel might know more. These CLs might be quite easy to backmerge AFAIR. |
EDIT: Seems like the separate fork is still a proposal. Please refer to https://github.com/nodejs/node/blob/master/doc/guides/maintaining-V8.md#backporting-to-abandoned-branches as a manual for backporting. @ofrobots is often coordinating this. |
I'm +1 on backporting for 5.4, however this would also be need to be backported to V8 5.5 and 5.6 upstream. @natorion How likely is upstream to approve the merge request for 5.5 and 5.6? |
@ofrobots 5.5 is abandoned as of this week. We'd need to backmerge ourselves, just like for 5.4. |
Status update on this one? |
I think we can close this PR because the performance problem is addressed upstream in V8. |
Checklist
Affected core subsystem(s)
Description of change
In V8, it is faster to create an array by
[]
instead ofnew Array()
.