timer: Improve performance of callbacks
setImmediate, setTimeout, and setInterval were called in an inefficient way, especially in the presence of arguments. This optimization improves their performance, with special cases for up to 4 arguments. Performance of setImmediate increases with 35%, setInterval with 60%, setTimeout with 70%. PR-URL: https://github.com/iojs/io.js/pull/406 Reviewed-by: Trevor Norris <trev.norris@gmail.com> Reviewed-by: Christian Tellnes <christian@tellnes.com>
This commit is contained in:
Родитель
6190a2236b
Коммит
bd1bd7e38d
128
lib/timers.js
128
lib/timers.js
|
@ -173,8 +173,9 @@ exports.active = function(item) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
exports.setTimeout = function(callback, after) {
|
exports.setTimeout = function(callback, after, arg1, arg2, arg3) {
|
||||||
var timer;
|
var timer, i, args;
|
||||||
|
var len = arguments.length;
|
||||||
|
|
||||||
after *= 1; // coalesce to number or NaN
|
after *= 1; // coalesce to number or NaN
|
||||||
|
|
||||||
|
@ -184,22 +185,38 @@ exports.setTimeout = function(callback, after) {
|
||||||
|
|
||||||
timer = new Timeout(after);
|
timer = new Timeout(after);
|
||||||
|
|
||||||
if (arguments.length <= 2) {
|
switch (len) {
|
||||||
timer._onTimeout = callback;
|
// fast cases
|
||||||
} else {
|
case 0:
|
||||||
/*
|
case 1:
|
||||||
* Sometimes setTimeout is called with arguments, EG
|
case 2:
|
||||||
*
|
timer._onTimeout = callback;
|
||||||
* setTimeout(callback, 2000, "hello", "world")
|
break;
|
||||||
*
|
case 3:
|
||||||
* If that's the case we need to call the callback with
|
timer._onTimeout = function() {
|
||||||
* those args. The overhead of an extra closure is not
|
callback.call(timer, arg1);
|
||||||
* desired in the normal case.
|
};
|
||||||
*/
|
break;
|
||||||
var args = Array.prototype.slice.call(arguments, 2);
|
case 4:
|
||||||
timer._onTimeout = function() {
|
timer._onTimeout = function() {
|
||||||
callback.apply(timer, args);
|
callback.call(timer, arg1, arg2);
|
||||||
};
|
};
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
timer._onTimeout = function() {
|
||||||
|
callback.call(timer, arg1, arg2, arg3);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
// slow case
|
||||||
|
default:
|
||||||
|
args = new Array(len - 2);
|
||||||
|
for (i = 2; i < len; i++)
|
||||||
|
args[i - 2] = arguments[i];
|
||||||
|
|
||||||
|
timer._onTimeout = function() {
|
||||||
|
callback.apply(timer, args);
|
||||||
|
};
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (process.domain) timer.domain = process.domain;
|
if (process.domain) timer.domain = process.domain;
|
||||||
|
@ -222,17 +239,24 @@ exports.clearTimeout = function(timer) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
exports.setInterval = function(callback, repeat) {
|
exports.setInterval = function(callback, repeat, arg1, arg2, arg3) {
|
||||||
repeat *= 1; // coalesce to number or NaN
|
repeat *= 1; // coalesce to number or NaN
|
||||||
|
|
||||||
if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) {
|
if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) {
|
||||||
repeat = 1; // schedule on next tick, follows browser behaviour
|
repeat = 1; // schedule on next tick, follows browser behaviour
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var args, i;
|
||||||
var timer = new Timeout(repeat);
|
var timer = new Timeout(repeat);
|
||||||
var args = Array.prototype.slice.call(arguments, 2);
|
var len = arguments.length - 2;
|
||||||
timer._onTimeout = wrapper;
|
timer._onTimeout = wrapper;
|
||||||
timer._repeat = true;
|
timer._repeat = true;
|
||||||
|
// Initialize args once for repeated invocation of slow case below
|
||||||
|
if (len > 3) {
|
||||||
|
args = new Array(len);
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
args[i] = arguments[i + 2];
|
||||||
|
}
|
||||||
|
|
||||||
if (process.domain) timer.domain = process.domain;
|
if (process.domain) timer.domain = process.domain;
|
||||||
exports.active(timer);
|
exports.active(timer);
|
||||||
|
@ -240,7 +264,25 @@ exports.setInterval = function(callback, repeat) {
|
||||||
return timer;
|
return timer;
|
||||||
|
|
||||||
function wrapper() {
|
function wrapper() {
|
||||||
callback.apply(this, args);
|
switch (len) {
|
||||||
|
// fast cases
|
||||||
|
case 0:
|
||||||
|
callback.call(this);
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
callback.call(this, arg1);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
callback.call(this, arg1, arg2);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
callback.call(this, arg1, arg2, arg3);
|
||||||
|
break;
|
||||||
|
// slow case
|
||||||
|
default:
|
||||||
|
callback.apply(this, args);
|
||||||
|
break;
|
||||||
|
}
|
||||||
// If callback called clearInterval().
|
// If callback called clearInterval().
|
||||||
if (timer._repeat === false) return;
|
if (timer._repeat === false) return;
|
||||||
// If timer is unref'd (or was - it's permanently removed from the list.)
|
// If timer is unref'd (or was - it's permanently removed from the list.)
|
||||||
|
@ -361,22 +403,44 @@ Immediate.prototype._idleNext = undefined;
|
||||||
Immediate.prototype._idlePrev = undefined;
|
Immediate.prototype._idlePrev = undefined;
|
||||||
|
|
||||||
|
|
||||||
exports.setImmediate = function(callback) {
|
exports.setImmediate = function(callback, arg1, arg2, arg3) {
|
||||||
|
var i, args;
|
||||||
|
var len = arguments.length;
|
||||||
var immediate = new Immediate();
|
var immediate = new Immediate();
|
||||||
var args, index;
|
|
||||||
|
|
||||||
L.init(immediate);
|
L.init(immediate);
|
||||||
|
|
||||||
immediate._onImmediate = callback;
|
switch (len) {
|
||||||
|
// fast cases
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
immediate._onImmediate = callback;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
immediate._onImmediate = function() {
|
||||||
|
callback.call(immediate, arg1);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
immediate._onImmediate = function() {
|
||||||
|
callback.call(immediate, arg1, arg2);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
immediate._onImmediate = function() {
|
||||||
|
callback.call(immediate, arg1, arg2, arg3);
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
// slow case
|
||||||
|
default:
|
||||||
|
args = new Array(len - 1);
|
||||||
|
for (i = 1; i < len; i++)
|
||||||
|
args[i - 1] = arguments[i];
|
||||||
|
|
||||||
if (arguments.length > 1) {
|
immediate._onImmediate = function() {
|
||||||
args = [];
|
callback.apply(immediate, args);
|
||||||
for (index = 1; index < arguments.length; index++)
|
};
|
||||||
args.push(arguments[index]);
|
break;
|
||||||
|
|
||||||
immediate._onImmediate = function() {
|
|
||||||
callback.apply(immediate, args);
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!process._needImmediateCallback) {
|
if (!process._needImmediateCallback) {
|
||||||
|
|
Загрузка…
Ссылка в новой задаче