diff options
author | Andras <andras@kinvey.com> | 2016-04-27 20:31:59 -0400 |
---|---|---|
committer | Jeremiah Senkpiel <fishrock123@rocketmail.com> | 2016-06-29 11:02:25 +0200 |
commit | 6f75b6672ca08c2bc3bf5fe14abb1a9648601e28 (patch) | |
tree | fc67efa679a469f0ddfd8c46a4f03ee0bb457c2b /lib/timers.js | |
parent | 6b0f86a8d519fd0780d2f6db2e7f28c2715dd795 (diff) | |
download | android-node-v8-6f75b6672ca08c2bc3bf5fe14abb1a9648601e28.tar.gz android-node-v8-6f75b6672ca08c2bc3bf5fe14abb1a9648601e28.tar.bz2 android-node-v8-6f75b6672ca08c2bc3bf5fe14abb1a9648601e28.zip |
timers: optimize `setImmediate()`
Save the setImmediate() callback arguments into an array instead of a
closure, and invoke the callback on the arguments from an optimizable
function.
60% faster setImmediate with 0 args (15% if self-recursive)
4x faster setImmediate with 1-3 args, 2x with > 3
seems to be faster with less memory pressure when memory is tight
Changes:
- use L.create() to build faster lists
- use runCallback() from within tryOnImmediate()
- save the arguments and do not build closures for the callbacks
PR-URL: https://github.com/nodejs/node/pull/6436
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Diffstat (limited to 'lib/timers.js')
-rw-r--r-- | lib/timers.js | 79 |
1 files changed, 47 insertions, 32 deletions
diff --git a/lib/timers.js b/lib/timers.js index 7ae25f6a32..9dbae32405 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -506,7 +506,7 @@ var immediateQueue = L.create(); function processImmediate() { - var queue = immediateQueue; + const queue = immediateQueue; var domain, immediate; immediateQueue = L.create(); @@ -515,9 +515,13 @@ function processImmediate() { immediate = L.shift(queue); domain = immediate.domain; + if (!immediate._onImmediate) + continue; + if (domain) domain.enter(); + immediate._callback = immediate._onImmediate; tryOnImmediate(immediate, queue); if (domain) @@ -538,7 +542,8 @@ function processImmediate() { function tryOnImmediate(immediate, queue) { var threw = true; try { - immediate._onImmediate(); + // make the actual call outside the try/catch to allow it to be optimized + runCallback(immediate); threw = false; } finally { if (threw && !L.isEmpty(queue)) { @@ -552,14 +557,36 @@ function tryOnImmediate(immediate, queue) { } } +function runCallback(timer) { + const argv = timer._argv; + const argc = argv ? argv.length : 0; + switch (argc) { + // fast-path callbacks with 0-3 arguments + case 0: + return timer._callback(); + case 1: + return timer._callback(argv[0]); + case 2: + return timer._callback(argv[0], argv[1]); + case 3: + return timer._callback(argv[0], argv[1], argv[2]); + // more than 3 arguments run slower with .apply + default: + return timer._callback.apply(timer, argv); + } +} -function Immediate() { } - -Immediate.prototype.domain = undefined; -Immediate.prototype._onImmediate = undefined; -Immediate.prototype._idleNext = undefined; -Immediate.prototype._idlePrev = undefined; +function Immediate() { + // assigning the callback here can cause optimize/deoptimize thrashing + // so have caller annotate the object (node v6.0.0, v8 5.0.71.35) + this._idleNext = null; + this._idlePrev = null; + this._callback = null; + this._argv = null; + this._onImmediate = null; + this.domain = process.domain; +} exports.setImmediate = function(callback, arg1, arg2, arg3) { if (typeof callback !== 'function') { @@ -567,52 +594,40 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) { } var i, args; - var len = arguments.length; - var immediate = new Immediate(); - - L.init(immediate); - switch (len) { + switch (arguments.length) { // fast cases case 0: case 1: - immediate._onImmediate = callback; break; case 2: - immediate._onImmediate = function() { - callback.call(immediate, arg1); - }; + args = [arg1]; break; case 3: - immediate._onImmediate = function() { - callback.call(immediate, arg1, arg2); - }; + args = [arg1, arg2]; break; case 4: - immediate._onImmediate = function() { - callback.call(immediate, arg1, arg2, arg3); - }; + args = [arg1, arg2, arg3]; break; // slow case default: - args = new Array(len - 1); - for (i = 1; i < len; i++) + args = [arg1, arg2, arg3]; + for (i = 4; i < arguments.length; i++) + // extend array dynamically, makes .apply run much faster in v6.0.0 args[i - 1] = arguments[i]; - - immediate._onImmediate = function() { - callback.apply(immediate, args); - }; break; } + // declaring it `const immediate` causes v6.0.0 to deoptimize this function + var immediate = new Immediate(); + immediate._callback = callback; + immediate._argv = args; + immediate._onImmediate = callback; if (!process._needImmediateCallback) { process._needImmediateCallback = true; process._immediateCallback = processImmediate; } - if (process.domain) - immediate.domain = process.domain; - L.append(immediateQueue, immediate); return immediate; |