events: remove emit micro-optimizations

With improvements in V8, using separate emit functions is no longer
necessary and can instead be replaced by the spread operator.

improvement                             confidence        p.value
events/ee-emit.js n=2000000                 2.98 %        0.09852489
events/ee-emit-2-args.js n=2000000          4.19 %    *** 0.0001914216
events/ee-emit-6-args.js n=2000000         61.69 %    *** 6.611964e-35
events/ee-emit-diff-args.js n=2000000      -0.36 %        0.305069
events/ee-once.js n=20000000                6.42 %    *** 1.27831e-06

PR-URL: https://github.com/nodejs/node/pull/16869
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Refael Ackermann <refack@gmail.com>
Reviewed-By: Evan Lucas <evanlucas@me.com>
Reviewed-By: Bryan English <bryan@bryanenglish.com>
Reviewed-By: Luigi Pinca <luigipinca@gmail.com>
Reviewed-By: Timothy Gu <timothygu99@gmail.com>
Reviewed-By: Franziska Hinkelmann <franziska.hinkelmann@gmail.com>
Reviewed-By: Benedikt Meurer <benedikt.meurer@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Brian White <mscdex@mscdex.net>
This commit is contained in:
Anatoli Papirovski 2017-11-07 13:22:49 -05:00
Родитель 6b351e92dc
Коммит f44f18a857
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 614E2E1ABEB4B2C0
4 изменённых файлов: 51 добавлений и 117 удалений

Просмотреть файл

@ -1,20 +0,0 @@
'use strict';
const common = require('../common.js');
const EventEmitter = require('events').EventEmitter;
const bench = common.createBenchmark(main, { n: [2e6] });
function main(conf) {
const n = conf.n | 0;
const ee = new EventEmitter();
for (var k = 0; k < 10; k += 1)
ee.on('dummy', function() {});
bench.start();
for (var i = 0; i < n; i += 1) {
ee.emit('dummy', 5, true);
}
bench.end(n);
}

Просмотреть файл

@ -2,19 +2,51 @@
const common = require('../common.js');
const EventEmitter = require('events').EventEmitter;
const bench = common.createBenchmark(main, { n: [2e6] });
const bench = common.createBenchmark(main, {
n: [2e6],
argc: [0, 2, 4, 10],
listeners: [1, 5, 10],
});
function main(conf) {
const n = conf.n | 0;
const argc = conf.argc | 0;
const listeners = Math.max(conf.listeners | 0, 1);
const ee = new EventEmitter();
for (var k = 0; k < 10; k += 1)
for (var k = 0; k < listeners; k += 1)
ee.on('dummy', function() {});
bench.start();
for (var i = 0; i < n; i += 1) {
ee.emit('dummy');
var i;
switch (argc) {
case 2:
bench.start();
for (i = 0; i < n; i += 1) {
ee.emit('dummy', true, 5);
}
bench.end(n);
break;
case 4:
bench.start();
for (i = 0; i < n; i += 1) {
ee.emit('dummy', true, 5, 10, false);
}
bench.end(n);
break;
case 10:
bench.start();
for (i = 0; i < n; i += 1) {
ee.emit('dummy', true, 5, 10, false, 5, 'string', true, false, 11, 20);
}
bench.end(n);
break;
default:
bench.start();
for (i = 0; i < n; i += 1) {
ee.emit('dummy');
}
bench.end(n);
break;
}
bench.end(n);
}

Просмотреть файл

@ -105,63 +105,6 @@ EventEmitter.prototype.getMaxListeners = function getMaxListeners() {
return $getMaxListeners(this);
};
// These standalone emit* functions are used to optimize calling of event
// handlers for fast cases because emit() itself often has a variable number of
// arguments and can be deoptimized because of that. These functions always have
// the same number of arguments and thus do not get deoptimized, so the code
// inside them can execute faster.
function emitNone(handler, isFn, self) {
if (isFn)
handler.call(self);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self);
}
}
function emitOne(handler, isFn, self, arg1) {
if (isFn)
handler.call(self, arg1);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1);
}
}
function emitTwo(handler, isFn, self, arg1, arg2) {
if (isFn)
handler.call(self, arg1, arg2);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2);
}
}
function emitThree(handler, isFn, self, arg1, arg2, arg3) {
if (isFn)
handler.call(self, arg1, arg2, arg3);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].call(self, arg1, arg2, arg3);
}
}
function emitMany(handler, isFn, self, args) {
if (isFn)
handler.apply(self, args);
else {
var len = handler.length;
var listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].apply(self, args);
}
}
EventEmitter.prototype.emit = function emit(type, ...args) {
let doError = (type === 'error');
@ -212,22 +155,13 @@ EventEmitter.prototype.emit = function emit(type, ...args) {
needDomainExit = true;
}
const isFn = typeof handler === 'function';
switch (args.length) {
case 0:
emitNone(handler, isFn, this);
break;
case 1:
emitOne(handler, isFn, this, args[0]);
break;
case 2:
emitTwo(handler, isFn, this, args[0], args[1]);
break;
case 3:
emitThree(handler, isFn, this, args[0], args[1], args[2]);
break;
default:
emitMany(handler, isFn, this, args);
if (typeof handler === 'function') {
handler.apply(this, args);
} else {
const len = handler.length;
const listeners = arrayClone(handler, len);
for (var i = 0; i < len; ++i)
listeners[i].apply(this, args);
}
if (needDomainExit)
@ -313,23 +247,11 @@ EventEmitter.prototype.prependListener =
return _addListener(this, type, listener, true);
};
function onceWrapper() {
function onceWrapper(...args) {
if (!this.fired) {
this.target.removeListener(this.type, this.wrapFn);
this.fired = true;
switch (arguments.length) {
case 0:
return this.listener.call(this.target);
case 1:
return this.listener.call(this.target, arguments[0]);
case 2:
return this.listener.call(this.target, arguments[0], arguments[1]);
case 3:
return this.listener.call(this.target, arguments[0], arguments[1],
arguments[2]);
default:
this.listener.apply(this.target, arguments);
}
this.listener.apply(this.target, args);
}
}

Просмотреть файл

@ -9,10 +9,10 @@ SyntaxError: Strict mode code may not include a with statement
at Module._compile (module.js:*:*)
at evalScript (bootstrap_node.js:*:*)
at Socket.<anonymous> (bootstrap_node.js:*:*)
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
at process._tickCallback (internal/process/next_tick.js:*:*)
42
42
[stdin]:1
@ -27,9 +27,9 @@ Error: hello
at Module._compile (module.js:*:*)
at evalScript (bootstrap_node.js:*:*)
at Socket.<anonymous> (bootstrap_node.js:*:*)
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
[stdin]:1
throw new Error("hello")
^
@ -42,9 +42,9 @@ Error: hello
at Module._compile (module.js:*:*)
at evalScript (bootstrap_node.js:*:*)
at Socket.<anonymous> (bootstrap_node.js:*:*)
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
100
[stdin]:1
var x = 100; y = x;
@ -58,9 +58,9 @@ ReferenceError: y is not defined
at Module._compile (module.js:*:*)
at evalScript (bootstrap_node.js:*:*)
at Socket.<anonymous> (bootstrap_node.js:*:*)
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
[stdin]:1
var ______________________________________________; throw 10