From 4c9dd6822eb9520588e4d06d251ce8e32469d4bc Mon Sep 17 00:00:00 2001 From: Brian White Date: Sat, 24 Dec 2016 21:58:36 -0500 Subject: [PATCH] events: improve once() performance This commit takes advantage of the performance improvements V8 has made to function.bind() in V8 5.4 and uses it to avoid constant recompilation/reoptimization of the wrapper closure used in once(). This change results in ~27% performance increase for once(). PR-URL: https://github.com/nodejs/node/pull/10445 Reviewed-By: Luigi Pinca Reviewed-By: Teddy Katz Reviewed-By: James M Snell Reviewed-By: Colin Ihrig --- benchmark/events/ee-once.js | 20 ++++++++++++++++++++ lib/events.js | 23 +++++++++++++---------- 2 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 benchmark/events/ee-once.js diff --git a/benchmark/events/ee-once.js b/benchmark/events/ee-once.js new file mode 100644 index 0000000000..29ea562c40 --- /dev/null +++ b/benchmark/events/ee-once.js @@ -0,0 +1,20 @@ +'use strict'; +var common = require('../common.js'); +var EventEmitter = require('events').EventEmitter; + +var bench = common.createBenchmark(main, {n: [2e7]}); + +function main(conf) { + var n = conf.n | 0; + + var ee = new EventEmitter(); + + function listener() {} + + bench.start(); + for (var i = 0; i < n; i += 1) { + ee.once('dummy', listener); + ee.emit('dummy'); + } + bench.end(n); +} diff --git a/lib/events.js b/lib/events.js index e7202bfedd..4ccec4e595 100644 --- a/lib/events.js +++ b/lib/events.js @@ -283,17 +283,20 @@ EventEmitter.prototype.prependListener = return _addListener(this, type, listener, true); }; -function _onceWrap(target, type, listener) { - var fired = false; - function g() { - target.removeListener(type, g); - if (!fired) { - fired = true; - listener.apply(target, arguments); - } +function onceWrapper() { + this.target.removeListener(this.type, this.wrapFn); + if (!this.fired) { + this.fired = true; + this.listener.apply(this.target, arguments); } - g.listener = listener; - return g; +} + +function _onceWrap(target, type, listener) { + var state = { fired: false, wrapFn: undefined, target, type, listener }; + var wrapped = onceWrapper.bind(state); + wrapped.listener = listener; + state.wrapFn = wrapped; + return wrapped; } EventEmitter.prototype.once = function once(type, listener) {