-
Notifications
You must be signed in to change notification settings - Fork 31.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
events: add fast and slow path #52733
base: main
Are you sure you want to change the base?
events: add fast and slow path #52733
Conversation
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.
Really nice, happy the idea works and awesome benchmrks. Still lots of work to do here and stuff to optimize.
I imgined something even faster (e.g. optimize for single event + single listener (+error listener) and not just single listener for the event).
In many cases (like events.once and .on) we can do even faster. Lots of low hanging fruit once we have two implementations.
I also envisioned something more akin to shapes (rather than just two representations), though seeing the benchmarks even just this iss already a huge win given how omnipresent events and emitters are in the ecoystem.
Would also be cool to apply this later to EventTarget and a few others
lib/events.js
Outdated
// 3. kErrorMonitor - undefined by default | ||
// TODO - add comment for what this is optimized for | ||
/** | ||
* This class is optimized for the case where there is only a single listener for each event. |
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 would also optimize for a few specific events.
If I use 1 class I will need to add an if in the hot path (emit) |
I need help with reducing the creation time for streams Cc @nodejs/performance |
lib/events.js
Outdated
// TODO(rluvaton): if have newListener and removeListener events, switch to slow path | ||
|
||
const shouldBeFastPath = arg === undefined || | ||
ObjectEntries(arg).some(({ 0: key, 1: value }) => |
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.
Can you avoid Object.entries
in 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.
Instead changed to a slow path by default to avoid potential bugs
As a side note, I think we might want to drop captureRejections. It was a nice experiment without major usage. It's code that sits into a hot path (even if not triggered), so maybe it would help speeding this up a bit. |
@@ -47,7 +47,6 @@ assert.throws(function() { | |||
}, /blerg/); | |||
|
|||
process.on('exit', function() { | |||
assert(!(myee._events instanceof Object)); |
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.
Removed this as in fast path we don't have __proto__: null
on the events and therefor it is an object
This comment was marked as outdated.
This comment was marked as outdated.
// Users can call emit in the constructor before even calling super causing this[kImpl] to be undefined | ||
const impl = this[kImpl]; | ||
|
||
// The order here is important as impl === undefined is slower than type === 'error' |
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 have no idea why checking type === 'error'
first is faster when running ee-emit.js
benchmark and slower is impl === undefined
first
assert.strictEqual(ee._events.hasOwnProperty, undefined); | ||
assert.strictEqual(ee._events.toString, undefined); |
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.
Removed this as in fast path we don't have __proto__: null
on the events and therefor it has those properties
@@ -26,7 +26,6 @@ const events = require('events'); | |||
|
|||
const e = new events.EventEmitter(); | |||
|
|||
assert(!(e._events instanceof Object)); |
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.
Removed this as in fast path we don't have __proto__: null
on the events and therefor it is an object
@@ -32,7 +32,6 @@ let fl; // foo listeners | |||
fl = e.listeners('foo'); | |||
assert(Array.isArray(fl)); | |||
assert.strictEqual(fl.length, 0); | |||
assert(!(e._events instanceof Object)); |
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.
Removed this as in fast path we don't have __proto__: null
on the events and therefor it is an object
5f26e0d
to
acf96af
Compare
this will break but just for the sake of it
Thank you @benjamingr for the idea
WIP!!!, fixed most of the tests
TODO
maybe split to multiple commits to keep the history of original events file
lib/domain.js
as it override emit functionkCapture
enabledBenchmarks
events
events
benchmark URLoutdated benchmark result
events
benchmark URLstreams
streams
benchmark URLoutdated benchmark result
streams
benchmark URL