-
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
events: several perf related adjustments #16212
Changes from all commits
adabed3
75571fb
00698a6
40a7e23
0585047
05c065f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,9 +54,6 @@ Object.defineProperty(EventEmitter, 'defaultMaxListeners', { | |
return defaultMaxListeners; | ||
}, | ||
set: function(arg) { | ||
// force global console to be compiled. | ||
// see https://github.com/nodejs/node/issues/4467 | ||
console; | ||
// check whether the input is a positive number (whose value is zero or | ||
// greater and not a NaN). | ||
if (typeof arg !== 'number' || arg < 0 || arg !== arg) { | ||
|
@@ -77,7 +74,8 @@ EventEmitter.init = function() { | |
} | ||
} | ||
|
||
if (!this._events || this._events === Object.getPrototypeOf(this)._events) { | ||
if (this._events === undefined || | ||
this._events === Object.getPrototypeOf(this)._events) { | ||
this._events = Object.create(null); | ||
this._eventsCount = 0; | ||
} | ||
|
@@ -163,24 +161,23 @@ function emitMany(handler, isFn, self, args) { | |
} | ||
} | ||
|
||
EventEmitter.prototype.emit = function emit(type) { | ||
var er, handler, len, args, i, events, domain; | ||
var needDomainExit = false; | ||
var doError = (type === 'error'); | ||
EventEmitter.prototype.emit = function emit(type, ...args) { | ||
let doError = (type === 'error'); | ||
|
||
events = this._events; | ||
if (events) | ||
doError = (doError && events.error == null); | ||
const events = this._events; | ||
if (events !== undefined) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Changes like this really warrant a CITGM run. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 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. |
||
doError = (doError && events.error === undefined); | ||
else if (!doError) | ||
return false; | ||
|
||
domain = this.domain; | ||
const domain = this.domain; | ||
|
||
// If there is no 'error' event listener then throw. | ||
if (doError) { | ||
if (arguments.length > 1) | ||
er = arguments[1]; | ||
if (domain) { | ||
let er; | ||
if (args.length > 0) | ||
er = args[0]; | ||
if (domain !== null && domain !== undefined) { | ||
if (!er) { | ||
const errors = lazyErrors(); | ||
er = new errors.Error('ERR_UNHANDLED_ERROR'); | ||
|
@@ -203,37 +200,32 @@ EventEmitter.prototype.emit = function emit(type) { | |
return false; | ||
} | ||
|
||
handler = events[type]; | ||
const handler = events[type]; | ||
|
||
if (!handler) | ||
if (handler === undefined) | ||
return false; | ||
|
||
if (domain && this !== process) { | ||
let needDomainExit = false; | ||
if (domain !== null && domain !== undefined && this !== process) { | ||
domain.enter(); | ||
needDomainExit = true; | ||
} | ||
|
||
var isFn = typeof handler === 'function'; | ||
len = arguments.length; | ||
switch (len) { | ||
// fast cases | ||
case 1: | ||
const isFn = typeof handler === 'function'; | ||
switch (args.length) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wait if we're using rest args, while the unwinding? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's still faster to have these unwound cases than just using I tested probably 6 different permutations of this, this one came out consistently the fastest. I had some that were faster for the more than 4 args case but slower for the fast cases. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you gist those permutations? BTW: I'm also seeing faster results for unwound calls, but <10% differance There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, the difference isn't massive but I'm not comfortable having a performance regression. Will put together a gist this weekend if I can recall exactly what it was I tested. |
||
case 0: | ||
emitNone(handler, isFn, this); | ||
break; | ||
case 1: | ||
emitOne(handler, isFn, this, args[0]); | ||
break; | ||
case 2: | ||
emitOne(handler, isFn, this, arguments[1]); | ||
emitTwo(handler, isFn, this, args[0], args[1]); | ||
break; | ||
case 3: | ||
emitTwo(handler, isFn, this, arguments[1], arguments[2]); | ||
break; | ||
case 4: | ||
emitThree(handler, isFn, this, arguments[1], arguments[2], arguments[3]); | ||
emitThree(handler, isFn, this, args[0], args[1], args[2]); | ||
break; | ||
// slower | ||
default: | ||
args = new Array(len - 1); | ||
for (i = 1; i < len; i++) | ||
args[i - 1] = arguments[i]; | ||
emitMany(handler, isFn, this, args); | ||
} | ||
|
||
|
@@ -254,13 +246,13 @@ function _addListener(target, type, listener, prepend) { | |
} | ||
|
||
events = target._events; | ||
if (!events) { | ||
if (events === undefined) { | ||
events = target._events = Object.create(null); | ||
target._eventsCount = 0; | ||
} else { | ||
// To avoid recursion in the case that type === "newListener"! Before | ||
// adding it to the listeners, first emit "newListener". | ||
if (events.newListener) { | ||
if (events.newListener !== undefined) { | ||
target.emit('newListener', type, | ||
listener.listener ? listener.listener : listener); | ||
|
||
|
@@ -271,7 +263,7 @@ function _addListener(target, type, listener, prepend) { | |
existing = events[type]; | ||
} | ||
|
||
if (!existing) { | ||
if (existing === undefined) { | ||
// Optimize the case of one listener. Don't need the extra array object. | ||
existing = events[type] = listener; | ||
++target._eventsCount; | ||
|
@@ -337,10 +329,7 @@ function onceWrapper() { | |
return this.listener.call(this.target, arguments[0], arguments[1], | ||
arguments[2]); | ||
default: | ||
const args = new Array(arguments.length); | ||
for (var i = 0; i < args.length; ++i) | ||
args[i] = arguments[i]; | ||
this.listener.apply(this.target, args); | ||
this.listener.apply(this.target, arguments); | ||
} | ||
} | ||
} | ||
|
@@ -385,11 +374,11 @@ EventEmitter.prototype.removeListener = | |
} | ||
|
||
events = this._events; | ||
if (!events) | ||
if (events === undefined) | ||
return this; | ||
|
||
list = events[type]; | ||
if (!list) | ||
if (list === undefined) | ||
return this; | ||
|
||
if (list === listener || list.listener === listener) { | ||
|
@@ -422,7 +411,7 @@ EventEmitter.prototype.removeListener = | |
if (list.length === 1) | ||
events[type] = list[0]; | ||
|
||
if (events.removeListener) | ||
if (events.removeListener !== undefined) | ||
this.emit('removeListener', type, originalListener || listener); | ||
} | ||
|
||
|
@@ -434,15 +423,15 @@ EventEmitter.prototype.removeAllListeners = | |
var listeners, events, i; | ||
|
||
events = this._events; | ||
if (!events) | ||
if (events === undefined) | ||
return this; | ||
|
||
// not listening for removeListener, no need to emit | ||
if (!events.removeListener) { | ||
if (events.removeListener === undefined) { | ||
if (arguments.length === 0) { | ||
this._events = Object.create(null); | ||
this._eventsCount = 0; | ||
} else if (events[type]) { | ||
} else if (events[type] !== undefined) { | ||
if (--this._eventsCount === 0) | ||
this._events = Object.create(null); | ||
else | ||
|
@@ -470,7 +459,7 @@ EventEmitter.prototype.removeAllListeners = | |
|
||
if (typeof listeners === 'function') { | ||
this.removeListener(type, listeners); | ||
} else if (listeners) { | ||
} else if (listeners !== undefined) { | ||
// LIFO order | ||
for (i = listeners.length - 1; i >= 0; i--) { | ||
this.removeListener(type, listeners[i]); | ||
|
@@ -481,23 +470,19 @@ EventEmitter.prototype.removeAllListeners = | |
}; | ||
|
||
EventEmitter.prototype.listeners = function listeners(type) { | ||
var evlistener; | ||
var ret; | ||
var events = this._events; | ||
const events = this._events; | ||
|
||
if (!events) | ||
ret = []; | ||
else { | ||
evlistener = events[type]; | ||
if (!evlistener) | ||
ret = []; | ||
else if (typeof evlistener === 'function') | ||
ret = [evlistener.listener || evlistener]; | ||
else | ||
ret = unwrapListeners(evlistener); | ||
} | ||
if (events === undefined) | ||
return []; | ||
|
||
return ret; | ||
const evlistener = events[type]; | ||
if (evlistener === undefined) | ||
return []; | ||
|
||
if (typeof evlistener === 'function') | ||
return [evlistener.listener || evlistener]; | ||
|
||
return unwrapListeners(evlistener); | ||
}; | ||
|
||
EventEmitter.listenerCount = function(emitter, type) { | ||
|
@@ -512,12 +497,12 @@ EventEmitter.prototype.listenerCount = listenerCount; | |
function listenerCount(type) { | ||
const events = this._events; | ||
|
||
if (events) { | ||
if (events !== undefined) { | ||
const evlistener = events[type]; | ||
|
||
if (typeof evlistener === 'function') { | ||
return 1; | ||
} else if (evlistener) { | ||
} else if (evlistener !== undefined) { | ||
return evlistener.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.
@bmeurer what do you think? As far as I understand, this is related to the escape analysis system. Do we need V8 62 to have the perf benefit?
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.
The escape analysis is responsible for avoiding allocations of either
arguments
object or rest parameters. If escape analysis is off, every invocation of this function will always have to allocatearguments
or rest parameters, even if it's not used.So, yes, for code like this, independent of whether you use
arguments
or rest parameters, escape analysis usually helps performance-wise.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 is the latest on Escape Analysis in 8.x LTS? I know there was an issue opened discussing it but can't tell by looking at it what was decided, if anything yet.