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
|
// create a new linked list
|
||||||
function create() {
|
function create() {
|
||||||
var list = { _idleNext: null, _idlePrev: null };
|
const list = { _idleNext: null, _idlePrev: null };
|
||||||
init(list);
|
init(list);
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -506,7 +506,7 @@ var immediateQueue = L.create();
|
|||||||
|
|
||||||
|
|
||||||
function processImmediate() {
|
function processImmediate() {
|
||||||
var queue = immediateQueue;
|
const queue = immediateQueue;
|
||||||
var domain, immediate;
|
var domain, immediate;
|
||||||
|
|
||||||
immediateQueue = L.create();
|
immediateQueue = L.create();
|
||||||
@ -515,9 +515,13 @@ function processImmediate() {
|
|||||||
immediate = L.shift(queue);
|
immediate = L.shift(queue);
|
||||||
domain = immediate.domain;
|
domain = immediate.domain;
|
||||||
|
|
||||||
|
if (!immediate._onImmediate)
|
||||||
|
continue;
|
||||||
|
|
||||||
if (domain)
|
if (domain)
|
||||||
domain.enter();
|
domain.enter();
|
||||||
|
|
||||||
|
immediate._callback = immediate._onImmediate;
|
||||||
tryOnImmediate(immediate, queue);
|
tryOnImmediate(immediate, queue);
|
||||||
|
|
||||||
if (domain)
|
if (domain)
|
||||||
@ -538,7 +542,8 @@ function processImmediate() {
|
|||||||
function tryOnImmediate(immediate, queue) {
|
function tryOnImmediate(immediate, queue) {
|
||||||
var threw = true;
|
var threw = true;
|
||||||
try {
|
try {
|
||||||
immediate._onImmediate();
|
// make the actual call outside the try/catch to allow it to be optimized
|
||||||
|
runCallback(immediate);
|
||||||
threw = false;
|
threw = false;
|
||||||
} finally {
|
} finally {
|
||||||
if (threw && !L.isEmpty(queue)) {
|
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) {
|
exports.setImmediate = function(callback, arg1, arg2, arg3) {
|
||||||
if (typeof callback !== 'function') {
|
if (typeof callback !== 'function') {
|
||||||
@ -567,52 +594,40 @@ exports.setImmediate = function(callback, arg1, arg2, arg3) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var i, args;
|
var i, args;
|
||||||
var len = arguments.length;
|
|
||||||
var immediate = new Immediate();
|
|
||||||
|
|
||||||
L.init(immediate);
|
switch (arguments.length) {
|
||||||
|
|
||||||
switch (len) {
|
|
||||||
// fast cases
|
// fast cases
|
||||||
case 0:
|
case 0:
|
||||||
case 1:
|
case 1:
|
||||||
immediate._onImmediate = callback;
|
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
immediate._onImmediate = function() {
|
args = [arg1];
|
||||||
callback.call(immediate, arg1);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
immediate._onImmediate = function() {
|
args = [arg1, arg2];
|
||||||
callback.call(immediate, arg1, arg2);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
immediate._onImmediate = function() {
|
args = [arg1, arg2, arg3];
|
||||||
callback.call(immediate, arg1, arg2, arg3);
|
|
||||||
};
|
|
||||||
break;
|
break;
|
||||||
// slow case
|
// slow case
|
||||||
default:
|
default:
|
||||||
args = new Array(len - 1);
|
args = [arg1, arg2, arg3];
|
||||||
for (i = 1; i < len; i++)
|
for (i = 4; i < arguments.length; i++)
|
||||||
|
// extend array dynamically, makes .apply run much faster in v6.0.0
|
||||||
args[i - 1] = arguments[i];
|
args[i - 1] = arguments[i];
|
||||||
|
|
||||||
immediate._onImmediate = function() {
|
|
||||||
callback.apply(immediate, args);
|
|
||||||
};
|
|
||||||
break;
|
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) {
|
if (!process._needImmediateCallback) {
|
||||||
process._needImmediateCallback = true;
|
process._needImmediateCallback = true;
|
||||||
process._immediateCallback = processImmediate;
|
process._immediateCallback = processImmediate;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.domain)
|
|
||||||
immediate.domain = process.domain;
|
|
||||||
|
|
||||||
L.append(immediateQueue, immediate);
|
L.append(immediateQueue, immediate);
|
||||||
|
|
||||||
return immediate;
|
return immediate;
|
||||||
|
@ -5,6 +5,7 @@ var assert = require('assert');
|
|||||||
let immediateA = false;
|
let immediateA = false;
|
||||||
let immediateB = false;
|
let immediateB = false;
|
||||||
let immediateC = [];
|
let immediateC = [];
|
||||||
|
let immediateD = [];
|
||||||
|
|
||||||
setImmediate(function() {
|
setImmediate(function() {
|
||||||
try {
|
try {
|
||||||
@ -25,8 +26,13 @@ setImmediate(function(x, y, z) {
|
|||||||
immediateC = [x, y, z];
|
immediateC = [x, y, z];
|
||||||
}, 1, 2, 3);
|
}, 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() {
|
process.on('exit', function() {
|
||||||
assert.ok(immediateA, 'Immediate should happen after normal execution');
|
assert.ok(immediateA, 'Immediate should happen after normal execution');
|
||||||
assert.notStrictEqual(immediateB, true, 'immediateB should not fire');
|
assert.notStrictEqual(immediateB, true, 'immediateB should not fire');
|
||||||
assert.deepStrictEqual(immediateC, [1, 2, 3], 'immediateC args should match');
|
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
|
// list
|
||||||
assert.ok(L.isEmpty(list));
|
assert.ok(L.isEmpty(list));
|
||||||
|
|
||||||
var list2 = L.create();
|
const list2 = L.create();
|
||||||
var list3 = L.create();
|
const list3 = L.create();
|
||||||
assert.ok(L.isEmpty(list2));
|
assert.ok(L.isEmpty(list2));
|
||||||
assert.ok(L.isEmpty(list3));
|
assert.ok(L.isEmpty(list3));
|
||||||
assert.ok(list2 != list3);
|
assert.ok(list2 != list3);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user