mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
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>
This commit is contained in:
parent
6b0f86a8d5
commit
6f75b6672c
@ -8,7 +8,7 @@ exports.init = init;
|
||||
|
||||
// create a new linked list
|
||||
function create() {
|
||||
var list = { _idleNext: null, _idlePrev: null };
|
||||
const list = { _idleNext: null, _idlePrev: null };
|
||||
init(list);
|
||||
return list;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -5,6 +5,7 @@ var assert = require('assert');
|
||||
let immediateA = false;
|
||||
let immediateB = false;
|
||||
let immediateC = [];
|
||||
let immediateD = [];
|
||||
|
||||
setImmediate(function() {
|
||||
try {
|
||||
@ -25,8 +26,13 @@ setImmediate(function(x, y, z) {
|
||||
immediateC = [x, y, z];
|
||||
}, 1, 2, 3);
|
||||
|
||||
setImmediate(function(x, y, z, a, b) {
|
||||
immediateD = [x, y, z, a, b];
|
||||
}, 1, 2, 3, 4, 5);
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.ok(immediateA, 'Immediate should happen after normal execution');
|
||||
assert.notStrictEqual(immediateB, true, 'immediateB should not fire');
|
||||
assert.deepStrictEqual(immediateC, [1, 2, 3], 'immediateC args should match');
|
||||
assert.deepStrictEqual(immediateD, [1, 2, 3, 4, 5], '5 args should match');
|
||||
});
|
||||
|
@ -103,8 +103,8 @@ assert.equal(C, L.shift(list));
|
||||
// list
|
||||
assert.ok(L.isEmpty(list));
|
||||
|
||||
var list2 = L.create();
|
||||
var list3 = L.create();
|
||||
const list2 = L.create();
|
||||
const list3 = L.create();
|
||||
assert.ok(L.isEmpty(list2));
|
||||
assert.ok(L.isEmpty(list3));
|
||||
assert.ok(list2 != list3);
|
||||
|
Loading…
Reference in New Issue
Block a user