| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Replace truthy/falsey checks of _events and _events[type] with comparisons to undefined for better performance: events/ee-add-remove.js n=250000 5.30 % *** 4.260028e-07 events/ee-emit.js n=2000000 4.18 % *** 1.026649e-05 This has a knock-on effect on modules that use lots of events, e.g.: http2/headers.js nheaders=0 n=1000 2.60 % *** 0.000298338
Previously, console had to be compiled in case it was not available but this is no longer necessary - remove it. Refs: nodejs#15111
There was a problem hiding this comment.
Why this was changed back to !events?
Sorry, something went wrong.
There was a problem hiding this comment.
Complete accident. Will push a fix shortly.
Sorry, something went wrong.
There was a problem hiding this comment.
Ditto.
Sorry, something went wrong.
There was a problem hiding this comment.
Same as above, total accident. Fixed now. Thanks for flagging!
Sorry, something went wrong.
|
I started a benchmark CI job, the 19 one (still in the queue at the moment): https://ci.nodejs.org/job/benchmark-node-micro-benchmarks/ |
Sorry, something went wrong.
There was a problem hiding this comment.
Changes like this really warrant a CITGM run.
Sorry, something went wrong.
There was a problem hiding this comment.
Yeah, I completely agree. 👍 I tried to flag it in my description of the PR but it's a bit of a wall of text.
Sorry, something went wrong.
|
Did you benchmark this against master or a node canary branch? I just checked using spread in master the other week for emit() and it was singificantly slower still. |
Sorry, something went wrong.
|
@mscdex Master. I mean it's slower if we completely switch to using spread (it loses out big time for the 0-3 cases) but this is sort of a hybrid approach. (When I say completely, I mean getting rid of the separate emit functions and inlining it with spread.) |
Sorry, something went wrong.
|
Benchmark CI results: events/ee-add-remove.js n=250000 3.96 % *** 3.698348e-19 events/ee-emit.js n=2000000 4.00 % *** 3.956165e-09 events/ee-emit-multi-args.js n=2000000 1.17 % *** 8.071210e-04 events/ee-listener-count-on-prototype.js n=50000000 9.38 % *** 7.890460e-04 events/ee-listeners.js n=5000000 3.07 % *** 2.906789e-07 events/ee-listeners-many.js n=5000000 5.37 % ** 4.763790e-03 events/ee-once.js n=20000000 3.80 % *** 3.043686e-13 Keep in mind we don't have a bench for passing emit more than 2 args. The ee-emit-multi-args is just 2 arguments and matches my results above. |
Sorry, something went wrong.
|
Have you tested this on current node v8.x as well? If v8.x does not get V8 6.2 then we want to make sure we don't include a performance regression in that branch. |
Sorry, something went wrong.
|
@mscdex Can test this shortly. Good point. |
Sorry, something went wrong.
events/ee-emit.js n=2000000 5.84 % *** 7.900974e-09 events/ee-emit-multi-args.js n=2000000 2.25 % *** 5.169845e-05 events/ee-emit-6-args.js n=2000000 20.76 % *** 3.152791e-32 Just a quick test on v8.x to make sure there aren't any regressions. That said, I think the most sensible approach would be to have a separate backport PR & run the benchmark CI there. The first commit in here needs to be backported anyway as it doesn't land cleanly. |
Sorry, something went wrong.
There was a problem hiding this comment.
The else conditions become obsolete here due to the return
Sorry, something went wrong.
There was a problem hiding this comment.
Speaking of, I think the whole thing can be adjusted to not be an 'else' since the first if returns. I'll force push an adjusted commit.
Sorry, something went wrong.
There was a problem hiding this comment.
That is what I implied 😄
Sorry, something went wrong.
Each conditional branch in EventEmitter.prototype.listeners assigns its return value to a variable ret which is returned at the end. Instead just return from within each branch. This is both clearer and more performant. events/ee-listeners.js n=5000000 3.65 % *** 3.359171e-10
With recent changes in V8, it is now as performant or faster to use spread parameter within EventEmitter.prototype.emit, especially in cases where looping over arguments is required. events/ee-emit.js n=2000000 4.40 % *** 1.505543e-06 events/ee-emit-1-arg.js n=2000000 2.16 % *** 2.434584e-10 events/ee-emit-2-args.js n=2000000 1.05 % ** 0.001764852 events/ee-emit-3-args.js n=2000000 2.18 % *** 3.234954e-08 events/ee-emit-6-args.js n=2000000 17.17 % *** 1.298702e-103 events/ee-emit-10-args.js n=2000000 17.14 % *** 1.144958e-97 This has a knock-on effect for modules that use events extensively, such as http2: http2/headers.js nheaders=0 n=1000 2.10 % *** 6.792106e-11
Due to changes in V8 in 6.0 & 6.1, it's no longer necessary to copy arguments to avoid deopt. Just call .apply with arguments. Retains fast cases for 0-3 arguments. events/ee-once-4-args.js n=20000000 11.58 % *** 1.310379e-05
|
Replaced all else & else if branches with if in EventEmitter.prototype.listeners since they all return. |
Sorry, something went wrong.
|
Could somebody please start a CITGM for this? Want to make sure the whole world doesn't burn because of the first commit that changes _events checks to compare to undefined. |
Sorry, something went wrong.
|
CITGM: https://ci.nodejs.org/view/Node.js-citgm/job/citgm-smoker/1012/ @apapirovski you should be able to it yourself. |
Sorry, something went wrong.
|
@lpinca not quite there yet, not until Wednesday :) Thanks for starting it! |
Sorry, something went wrong.
|
No clue how to interpret the results. No obvious red flags but some of these failures seem to be a regular occurrence and with others I can't tell. |
Sorry, something went wrong.
|
Yes, honestly I also don't know how to interpret CITGM results. Not sure what are the expected failures nowadays. |
Sorry, something went wrong.
Due to changes in V8 in 6.0 & 6.1, it's no longer necessary to copy arguments to avoid deopt. Just call .apply with arguments. Retains fast cases for 0-3 arguments. events/ee-once-4-args.js n=20000000 11.58 % *** 1.310379e-05 PR-URL: nodejs#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Sorry, something went wrong.
|
@jasnell I would love to see this land in 9.0.0 if at all possible so it can begin baking immediately. Thanks! |
Sorry, something went wrong.
|
This does not land cleanly in 8.x It would need to be manually backported. As it seems this perf improvement needs some time to bake we may want to wait for it to live in 9.x for a bit before backporting |
Sorry, something went wrong.
|
@MylesBorins this needs to be benchmarked again when landed on 8. It likely needs V8 6.2. |
Sorry, something went wrong.
|
@mcarter @MylesBorins This has been tested against v8.x already as @mscdex requested it. Most of the perf improvements were nearly identical. |
Sorry, something went wrong.
|
Actually I don't think it was tested on V8 6.2. Master still was on V8 6.1 when you published the benchmark numbers |
Sorry, something went wrong.
|
@targos I think we had escape analysis enabled on master at that point |
Sorry, something went wrong.
Replace truthy/falsey checks of _events and _events[type] with comparisons to undefined for better performance: events/ee-add-remove.js n=250000 5.30 % *** 4.260028e-07 events/ee-emit.js n=2000000 4.18 % *** 1.026649e-05 This has a knock-on effect on modules that use lots of events, e.g.: http2/headers.js nheaders=0 n=1000 2.60 % *** 0.000298338 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Previously, console had to be compiled in case it was not available but this is no longer necessary - remove it. PR-URL: nodejs/node#16212 Refs: nodejs/node#15111 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Each conditional branch in EventEmitter.prototype.listeners assigns its return value to a variable ret which is returned at the end. Instead just return from within each branch. This is both clearer and more performant. events/ee-listeners.js n=5000000 3.65 % *** 3.359171e-10 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
With recent changes in V8, it is now as performant or faster to use spread parameter within EventEmitter.prototype.emit, especially in cases where looping over arguments is required. events/ee-emit.js n=2000000 4.40 % *** 1.505543e-06 events/ee-emit-1-arg.js n=2000000 2.16 % *** 2.434584e-10 events/ee-emit-2-args.js n=2000000 1.05 % ** 0.001764852 events/ee-emit-3-args.js n=2000000 2.18 % *** 3.234954e-08 events/ee-emit-6-args.js n=2000000 17.17 % *** 1.298702e-103 events/ee-emit-10-args.js n=2000000 17.14 % *** 1.144958e-97 This has a knock-on effect for modules that use events extensively, such as http2: http2/headers.js nheaders=0 n=1000 2.10 % *** 6.792106e-11 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Due to changes in V8 in 6.0 & 6.1, it's no longer necessary to copy arguments to avoid deopt. Just call .apply with arguments. Retains fast cases for 0-3 arguments. events/ee-once-4-args.js n=20000000 11.58 % *** 1.310379e-05 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Replace truthy/falsey checks of _events and _events[type] with comparisons to undefined for better performance: events/ee-add-remove.js n=250000 5.30 % *** 4.260028e-07 events/ee-emit.js n=2000000 4.18 % *** 1.026649e-05 This has a knock-on effect on modules that use lots of events, e.g.: http2/headers.js nheaders=0 n=1000 2.60 % *** 0.000298338 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Previously, console had to be compiled in case it was not available but this is no longer necessary - remove it. PR-URL: nodejs/node#16212 Refs: nodejs/node#15111 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Each conditional branch in EventEmitter.prototype.listeners assigns its return value to a variable ret which is returned at the end. Instead just return from within each branch. This is both clearer and more performant. events/ee-listeners.js n=5000000 3.65 % *** 3.359171e-10 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
With recent changes in V8, it is now as performant or faster to use spread parameter within EventEmitter.prototype.emit, especially in cases where looping over arguments is required. events/ee-emit.js n=2000000 4.40 % *** 1.505543e-06 events/ee-emit-1-arg.js n=2000000 2.16 % *** 2.434584e-10 events/ee-emit-2-args.js n=2000000 1.05 % ** 0.001764852 events/ee-emit-3-args.js n=2000000 2.18 % *** 3.234954e-08 events/ee-emit-6-args.js n=2000000 17.17 % *** 1.298702e-103 events/ee-emit-10-args.js n=2000000 17.14 % *** 1.144958e-97 This has a knock-on effect for modules that use events extensively, such as http2: http2/headers.js nheaders=0 n=1000 2.10 % *** 6.792106e-11 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
Due to changes in V8 in 6.0 & 6.1, it's no longer necessary to copy arguments to avoid deopt. Just call .apply with arguments. Retains fast cases for 0-3 arguments. events/ee-once-4-args.js n=20000000 11.58 % *** 1.310379e-05 PR-URL: nodejs/node#16212 Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Refael Ackermann <refack@gmail.com>
|
Quick ping to see if we want to include this in v8.x |
Sorry, something went wrong.
|
At this point I don't see a backport to 8.x as being critical |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
This PR covers a few different perf related changes to EventEmitter. Everything is separated into stand-alone commits. Might be easier to review as such but overall there actually there aren't that many lines being changed so it should be pretty easy to get through.
All performance benchmarks cited below are based on 100 or 200 sets (as necessary to get the right certainty) and are independent of other changes within the PR (so something like the onceWrapper result is strictly for that change and does not include the changes that precede it).
Feedback & reviews greatly appreciated!
events: stricter prop & variable checks for perf
Replace truthy/falsey checks of EventEmitter.prototype._events and EventEmitter.prototype._events[type] with comparisons to undefined for better performance:
This has a knock-on effect on modules that use lots of events, e.g.:
It's possible this could affect user-land code if users are interacting with _events directly. We should strongly consider running CITGM. Maybe this is semver-major? Not sure since it's not documented and leading _ obviously signifies a private prop.
events: remove unnecessary console instantiation
Previously, console had to be compiled in case it was not available but this is no longer necessary - remove it.
Refs: #15111
events: return values directly in listeners
Each conditional branch in EventEmitter.prototype.listeners assigns its return value to a variable ret which is returned at the end. Instead just return from within each branch. This is both clearer and more performant.
events: use spread function param in emit
With recent changes in V8, it is now as performant or faster to use spread parameter within EventEmitter.prototype.emit, especially in cases where looping over arguments is required.
This has a knock-on effect for modules that use events extensively, such as http2:
events: onceWrapper apply directly with arguments
Due to changes in V8, it's no longer necessary to copy arguments to avoid deopt. Just call apply with arguments. Retains fast cases for 0-3 arguments.
Checklist
Affected core subsystem(s)
events