Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit efb7a90

Browse filesBrowse files
AndrasFishrock123
authored andcommitted
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: #6436 Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
1 parent a5d8945 commit efb7a90
Copy full SHA for efb7a90

File tree

Expand file treeCollapse file tree

4 files changed

+56
-35
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

4 files changed

+56
-35
lines changed
Open diff view settings
Collapse file

‎lib/internal/linkedlist.js‎

Copy file name to clipboardExpand all lines: lib/internal/linkedlist.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ exports.init = init;
88

99
// create a new linked list
1010
function create() {
11-
var list = { _idleNext: null, _idlePrev: null };
11+
const list = { _idleNext: null, _idlePrev: null };
1212
init(list);
1313
return list;
1414
}
Collapse file

‎lib/timers.js‎

Copy file name to clipboardExpand all lines: lib/timers.js
+47-32Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ var immediateQueue = L.create();
506506

507507

508508
function processImmediate() {
509-
var queue = immediateQueue;
509+
const queue = immediateQueue;
510510
var domain, immediate;
511511

512512
immediateQueue = L.create();
@@ -515,9 +515,13 @@ function processImmediate() {
515515
immediate = L.shift(queue);
516516
domain = immediate.domain;
517517

518+
if (!immediate._onImmediate)
519+
continue;
520+
518521
if (domain)
519522
domain.enter();
520523

524+
immediate._callback = immediate._onImmediate;
521525
tryOnImmediate(immediate, queue);
522526

523527
if (domain)
@@ -538,7 +542,8 @@ function processImmediate() {
538542
function tryOnImmediate(immediate, queue) {
539543
var threw = true;
540544
try {
541-
immediate._onImmediate();
545+
// make the actual call outside the try/catch to allow it to be optimized
546+
runCallback(immediate);
542547
threw = false;
543548
} finally {
544549
if (threw && !L.isEmpty(queue)) {
@@ -552,67 +557,77 @@ function tryOnImmediate(immediate, queue) {
552557
}
553558
}
554559

560+
function runCallback(timer) {
561+
const argv = timer._argv;
562+
const argc = argv ? argv.length : 0;
563+
switch (argc) {
564+
// fast-path callbacks with 0-3 arguments
565+
case 0:
566+
return timer._callback();
567+
case 1:
568+
return timer._callback(argv[0]);
569+
case 2:
570+
return timer._callback(argv[0], argv[1]);
571+
case 3:
572+
return timer._callback(argv[0], argv[1], argv[2]);
573+
// more than 3 arguments run slower with .apply
574+
default:
575+
return timer._callback.apply(timer, argv);
576+
}
577+
}
555578

556-
function Immediate() { }
557-
558-
Immediate.prototype.domain = undefined;
559-
Immediate.prototype._onImmediate = undefined;
560-
Immediate.prototype._idleNext = undefined;
561-
Immediate.prototype._idlePrev = undefined;
562579

580+
function Immediate() {
581+
// assigning the callback here can cause optimize/deoptimize thrashing
582+
// so have caller annotate the object (node v6.0.0, v8 5.0.71.35)
583+
this._idleNext = null;
584+
this._idlePrev = null;
585+
this._callback = null;
586+
this._argv = null;
587+
this._onImmediate = null;
588+
this.domain = process.domain;
589+
}
563590

564591
exports.setImmediate = function(callback, arg1, arg2, arg3) {
565592
if (typeof callback !== 'function') {
566593
throw new TypeError('"callback" argument must be a function');
567594
}
568595

569596
var i, args;
570-
var len = arguments.length;
571-
var immediate = new Immediate();
572-
573-
L.init(immediate);
574597

575-
switch (len) {
598+
switch (arguments.length) {
576599
// fast cases
577600
case 0:
578601
case 1:
579-
immediate._onImmediate = callback;
580602
break;
581603
case 2:
582-
immediate._onImmediate = function() {
583-
callback.call(immediate, arg1);
584-
};
604+
args = [arg1];
585605
break;
586606
case 3:
587-
immediate._onImmediate = function() {
588-
callback.call(immediate, arg1, arg2);
589-
};
607+
args = [arg1, arg2];
590608
break;
591609
case 4:
592-
immediate._onImmediate = function() {
593-
callback.call(immediate, arg1, arg2, arg3);
594-
};
610+
args = [arg1, arg2, arg3];
595611
break;
596612
// slow case
597613
default:
598-
args = new Array(len - 1);
599-
for (i = 1; i < len; i++)
614+
args = [arg1, arg2, arg3];
615+
for (i = 4; i < arguments.length; i++)
616+
// extend array dynamically, makes .apply run much faster in v6.0.0
600617
args[i - 1] = arguments[i];
601-
602-
immediate._onImmediate = function() {
603-
callback.apply(immediate, args);
604-
};
605618
break;
606619
}
620+
// declaring it `const immediate` causes v6.0.0 to deoptimize this function
621+
var immediate = new Immediate();
622+
immediate._callback = callback;
623+
immediate._argv = args;
624+
immediate._onImmediate = callback;
607625

608626
if (!process._needImmediateCallback) {
609627
process._needImmediateCallback = true;
610628
process._immediateCallback = processImmediate;
611629
}
612630

613-
if (process.domain)
614-
immediate.domain = process.domain;
615-
616631
L.append(immediateQueue, immediate);
617632

618633
return immediate;
Collapse file

‎test/parallel/test-timers-immediate.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-timers-immediate.js
+6Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var assert = require('assert');
55
let immediateA = false;
66
let immediateB = false;
77
let immediateC = [];
8+
let immediateD = [];
89

910
setImmediate(function() {
1011
try {
@@ -25,8 +26,13 @@ setImmediate(function(x, y, z) {
2526
immediateC = [x, y, z];
2627
}, 1, 2, 3);
2728

29+
setImmediate(function(x, y, z, a, b) {
30+
immediateD = [x, y, z, a, b];
31+
}, 1, 2, 3, 4, 5);
32+
2833
process.on('exit', function() {
2934
assert.ok(immediateA, 'Immediate should happen after normal execution');
3035
assert.notStrictEqual(immediateB, true, 'immediateB should not fire');
3136
assert.deepStrictEqual(immediateC, [1, 2, 3], 'immediateC args should match');
37+
assert.deepStrictEqual(immediateD, [1, 2, 3, 4, 5], '5 args should match');
3238
});
Collapse file

‎test/parallel/test-timers-linked-list.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-timers-linked-list.js
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,8 +103,8 @@ assert.equal(C, L.shift(list));
103103
// list
104104
assert.ok(L.isEmpty(list));
105105

106-
var list2 = L.create();
107-
var list3 = L.create();
106+
const list2 = L.create();
107+
const list3 = L.create();
108108
assert.ok(L.isEmpty(list2));
109109
assert.ok(L.isEmpty(list3));
110110
assert.ok(list2 != list3);

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.