Skip to content

Commit efb7a90

Browse 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 <[email protected]> Reviewed-By: Jeremiah Senkpiel <[email protected]>
1 parent a5d8945 commit efb7a90

File tree

4 files changed

+56
-35
lines changed

4 files changed

+56
-35
lines changed

‎lib/internal/linkedlist.js‎

Lines 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
functioncreate(){
11-
varlist={_idleNext: null,_idlePrev: null};
11+
constlist={_idleNext: null,_idlePrev: null};
1212
init(list);
1313
returnlist;
1414
}

‎lib/timers.js‎

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -506,7 +506,7 @@ var immediateQueue = L.create();
506506

507507

508508
functionprocessImmediate(){
509-
varqueue=immediateQueue;
509+
constqueue=immediateQueue;
510510
vardomain,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
functiontryOnImmediate(immediate,queue){
539543
varthrew=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+
functionrunCallback(timer){
561+
constargv=timer._argv;
562+
constargc=argv ? argv.length : 0;
563+
switch(argc){
564+
// fast-path callbacks with 0-3 arguments
565+
case0:
566+
returntimer._callback();
567+
case1:
568+
returntimer._callback(argv[0]);
569+
case2:
570+
returntimer._callback(argv[0],argv[1]);
571+
case3:
572+
returntimer._callback(argv[0],argv[1],argv[2]);
573+
// more than 3 arguments run slower with .apply
574+
default:
575+
returntimer._callback.apply(timer,argv);
576+
}
577+
}
555578

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

580+
functionImmediate(){
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(typeofcallback!=='function'){
566593
thrownewTypeError('"callback" argument must be a function');
567594
}
568595

569596
vari,args;
570-
varlen=arguments.length;
571-
varimmediate=newImmediate();
572-
573-
L.init(immediate);
574597

575-
switch(len){
598+
switch(arguments.length){
576599
// fast cases
577600
case0:
578601
case1:
579-
immediate._onImmediate=callback;
580602
break;
581603
case2:
582-
immediate._onImmediate=function(){
583-
callback.call(immediate,arg1);
584-
};
604+
args=[arg1];
585605
break;
586606
case3:
587-
immediate._onImmediate=function(){
588-
callback.call(immediate,arg1,arg2);
589-
};
607+
args=[arg1,arg2];
590608
break;
591609
case4:
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=newArray(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+
varimmediate=newImmediate();
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
returnimmediate;

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var assert = require('assert');
55
letimmediateA=false;
66
letimmediateB=false;
77
letimmediateC=[];
8+
letimmediateD=[];
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
});

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

Lines 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-
varlist2=L.create();
107-
varlist3=L.create();
106+
constlist2=L.create();
107+
constlist3=L.create();
108108
assert.ok(L.isEmpty(list2));
109109
assert.ok(L.isEmpty(list3));
110110
assert.ok(list2!=list3);

0 commit comments

Comments
(0)