Skip to content

Commit bd1bd7e

Browse files
RubenVerborghtrevnorris
authored andcommitted
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: #406 Reviewed-by: Trevor Norris <[email protected]> Reviewed-by: Christian Tellnes <[email protected]>
1 parent 6190a22 commit bd1bd7e

File tree

1 file changed

+97
-33
lines changed

1 file changed

+97
-33
lines changed

lib/timers.js

+97-33
Original file line numberDiff line numberDiff line change
@@ -173,8 +173,9 @@ exports.active = function(item) {
173173
*/
174174

175175

176-
exports.setTimeout = function(callback, after) {
177-
var timer;
176+
exports.setTimeout = function(callback, after, arg1, arg2, arg3) {
177+
var timer, i, args;
178+
var len = arguments.length;
178179

179180
after *= 1; // coalesce to number or NaN
180181

@@ -184,22 +185,38 @@ exports.setTimeout = function(callback, after) {
184185

185186
timer = new Timeout(after);
186187

187-
if (arguments.length <= 2) {
188-
timer._onTimeout = callback;
189-
} else {
190-
/*
191-
* Sometimes setTimeout is called with arguments, EG
192-
*
193-
* setTimeout(callback, 2000, "hello", "world")
194-
*
195-
* If that's the case we need to call the callback with
196-
* those args. The overhead of an extra closure is not
197-
* desired in the normal case.
198-
*/
199-
var args = Array.prototype.slice.call(arguments, 2);
200-
timer._onTimeout = function() {
201-
callback.apply(timer, args);
202-
};
188+
switch (len) {
189+
// fast cases
190+
case 0:
191+
case 1:
192+
case 2:
193+
timer._onTimeout = callback;
194+
break;
195+
case 3:
196+
timer._onTimeout = function() {
197+
callback.call(timer, arg1);
198+
};
199+
break;
200+
case 4:
201+
timer._onTimeout = function() {
202+
callback.call(timer, arg1, arg2);
203+
};
204+
break;
205+
case 5:
206+
timer._onTimeout = function() {
207+
callback.call(timer, arg1, arg2, arg3);
208+
};
209+
break;
210+
// slow case
211+
default:
212+
args = new Array(len - 2);
213+
for (i = 2; i < len; i++)
214+
args[i - 2] = arguments[i];
215+
216+
timer._onTimeout = function() {
217+
callback.apply(timer, args);
218+
};
219+
break;
203220
}
204221

205222
if (process.domain) timer.domain = process.domain;
@@ -222,25 +239,50 @@ exports.clearTimeout = function(timer) {
222239
};
223240

224241

225-
exports.setInterval = function(callback, repeat) {
242+
exports.setInterval = function(callback, repeat, arg1, arg2, arg3) {
226243
repeat *= 1; // coalesce to number or NaN
227244

228245
if (!(repeat >= 1 && repeat <= TIMEOUT_MAX)) {
229246
repeat = 1; // schedule on next tick, follows browser behaviour
230247
}
231248

249+
var args, i;
232250
var timer = new Timeout(repeat);
233-
var args = Array.prototype.slice.call(arguments, 2);
251+
var len = arguments.length - 2;
234252
timer._onTimeout = wrapper;
235253
timer._repeat = true;
254+
// Initialize args once for repeated invocation of slow case below
255+
if (len > 3) {
256+
args = new Array(len);
257+
for (i = 0; i < len; i++)
258+
args[i] = arguments[i + 2];
259+
}
236260

237261
if (process.domain) timer.domain = process.domain;
238262
exports.active(timer);
239263

240264
return timer;
241265

242266
function wrapper() {
243-
callback.apply(this, args);
267+
switch (len) {
268+
// fast cases
269+
case 0:
270+
callback.call(this);
271+
break;
272+
case 1:
273+
callback.call(this, arg1);
274+
break;
275+
case 2:
276+
callback.call(this, arg1, arg2);
277+
break;
278+
case 3:
279+
callback.call(this, arg1, arg2, arg3);
280+
break;
281+
// slow case
282+
default:
283+
callback.apply(this, args);
284+
break;
285+
}
244286
// If callback called clearInterval().
245287
if (timer._repeat === false) return;
246288
// If timer is unref'd (or was - it's permanently removed from the list.)
@@ -361,22 +403,44 @@ Immediate.prototype._idleNext = undefined;
361403
Immediate.prototype._idlePrev = undefined;
362404

363405

364-
exports.setImmediate = function(callback) {
406+
exports.setImmediate = function(callback, arg1, arg2, arg3) {
407+
var i, args;
408+
var len = arguments.length;
365409
var immediate = new Immediate();
366-
var args, index;
367410

368411
L.init(immediate);
369412

370-
immediate._onImmediate = callback;
371-
372-
if (arguments.length > 1) {
373-
args = [];
374-
for (index = 1; index < arguments.length; index++)
375-
args.push(arguments[index]);
376-
377-
immediate._onImmediate = function() {
378-
callback.apply(immediate, args);
379-
};
413+
switch (len) {
414+
// fast cases
415+
case 0:
416+
case 1:
417+
immediate._onImmediate = callback;
418+
break;
419+
case 2:
420+
immediate._onImmediate = function() {
421+
callback.call(immediate, arg1);
422+
};
423+
break;
424+
case 3:
425+
immediate._onImmediate = function() {
426+
callback.call(immediate, arg1, arg2);
427+
};
428+
break;
429+
case 4:
430+
immediate._onImmediate = function() {
431+
callback.call(immediate, arg1, arg2, arg3);
432+
};
433+
break;
434+
// slow case
435+
default:
436+
args = new Array(len - 1);
437+
for (i = 1; i < len; i++)
438+
args[i - 1] = arguments[i];
439+
440+
immediate._onImmediate = function() {
441+
callback.apply(immediate, args);
442+
};
443+
break;
380444
}
381445

382446
if (!process._needImmediateCallback) {

0 commit comments

Comments
 (0)