Skip to content

Commit

Permalink
node: improve nextTick performance
Browse files Browse the repository at this point in the history
This commit uses separate functions to isolate deopts caused by
try-catches and avoids fn.apply() for callbacks with small numbers
of arguments.

These changes improve performance by ~2-16% in the various
nextTick benchmarks.
  • Loading branch information
mscdex committed May 1, 2015
1 parent 0450ce7 commit d09cb2f
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 25 deletions.
21 changes: 21 additions & 0 deletions benchmark/misc/next-tick-breadth-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@

var common = require('../common.js');
var bench = common.createBenchmark(main, {
millions: [2]
});

function main(conf) {
var N = +conf.millions * 1e6;
var n = 0;

function cb(arg1, arg2, arg3) {
n++;
if (n === N)
bench.end(n / 1e6);
}

bench.start();
for (var i = 0; i < N; i++) {
process.nextTick(cb, 512, true, null);
}
}
19 changes: 19 additions & 0 deletions benchmark/misc/next-tick-depth-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var common = require('../common.js');
var bench = common.createBenchmark(main, {
millions: [2]
});

process.maxTickDepth = Infinity;

function main(conf) {
var n = +conf.millions * 1e6;

bench.start();
process.nextTick(onNextTick, 512, true, null);
function onNextTick(arg1, arg2, arg3) {
if (--n)
process.nextTick(onNextTick);
else
bench.end(+conf.millions);
}
}
107 changes: 85 additions & 22 deletions src/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -328,23 +328,32 @@
// Run callbacks that have no domain.
// Using domains will cause this to be overridden.
function _tickCallback() {
var callback, threw, tock;
var callback, args, threw, tock;

do {
while (tickInfo[kIndex] < tickInfo[kLength]) {
tock = nextTickQueue[tickInfo[kIndex]++];
callback = tock.callback;
threw = true;
try {
if (tock.args === undefined)
callback();
else
callback.apply(null, tock.args);
threw = false;
} finally {
if (threw)
tickDone();
args = tock.args;
if (args === undefined)
threw = doNTCallback0(callback);
else {
switch (args.length) {
case 1:
threw = doNTCallback1(callback, args[0]);
break;
case 2:
threw = doNTCallback2(callback, args[0], args[1]);
break;
case 3:
threw = doNTCallback3(callback, args[0], args[1], args[2]);
break;
default:
threw = doNTCallbackMany(callback, args);
}
}
if (threw)
tickDone();
if (1e4 < tickInfo[kIndex])
tickDone();
}
Expand All @@ -355,26 +364,35 @@
}

function _tickDomainCallback() {
var callback, domain, threw, tock;
var callback, domain, args, threw, tock;

do {
while (tickInfo[kIndex] < tickInfo[kLength]) {
tock = nextTickQueue[tickInfo[kIndex]++];
callback = tock.callback;
domain = tock.domain;
args = tock.args;
if (domain)
domain.enter();
threw = true;
try {
if (tock.args === undefined)
callback();
else
callback.apply(null, tock.args);
threw = false;
} finally {
if (threw)
tickDone();
if (args === undefined)
threw = doNTCallback0(callback);
else {
switch (args.length) {
case 1:
threw = doNTCallback1(callback, args[0]);
break;
case 2:
threw = doNTCallback2(callback, args[0], args[1]);
break;
case 3:
threw = doNTCallback3(callback, args[0], args[1], args[2]);
break;
default:
threw = doNTCallbackMany(callback, args);
}
}
if (threw)
tickDone();
if (1e4 < tickInfo[kIndex])
tickDone();
if (domain)
Expand All @@ -386,6 +404,51 @@
} while (tickInfo[kLength] !== 0);
}

function doNTCallback0(callback) {
var threw = true;
try {
callback();
threw = false;
} finally {}
return threw;
}

function doNTCallback1(callback, arg1) {
var threw = true;
try {
callback(arg1);
threw = false;
} finally {}
return threw;
}

function doNTCallback2(callback, arg1, arg2) {
var threw = true;
try {
callback(arg1, arg2);
threw = false;
} finally {}
return threw;
}

function doNTCallback3(callback, arg1, arg2, arg3) {
var threw = true;
try {
callback(arg1, arg2, arg3);
threw = false;
} finally {}
return threw;
}

function doNTCallbackMany(callback, args) {
var threw = true;
try {
callback.apply(null, args);
threw = false;
} finally {}
return threw;
}

function TickObject(c, args) {
this.callback = c;
this.domain = process.domain || null;
Expand Down
1 change: 1 addition & 0 deletions test/message/nexttick_throw.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
^
ReferenceError: undefined_reference_error_maker is not defined
at *test*message*nexttick_throw.js:*:*
at doNTCallback0 (node.js:*:*)
at process._tickCallback (node.js:*:*)
at Function.Module.runMain (module.js:*:*)
at startup (node.js:*:*)
Expand Down
7 changes: 4 additions & 3 deletions test/message/stdin_messages.out
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ SyntaxError: Strict mode code may not include a with statement
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at doNTCallback2 (node.js:*:*)
at process._tickCallback (node.js:*:*)
42
42
Expand All @@ -29,7 +30,7 @@ Error: hello
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at process._tickCallback (node.js:*:*)
at doNTCallback2 (node.js:*:*)

[stdin]:1
throw new Error("hello")
Expand All @@ -44,7 +45,7 @@ Error: hello
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at process._tickCallback (node.js:*:*)
at doNTCallback2 (node.js:*:*)
100

[stdin]:1
Expand All @@ -60,7 +61,7 @@ ReferenceError: y is not defined
at emitNone (events.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at process._tickCallback (node.js:*:*)
at doNTCallback2 (node.js:*:*)

[stdin]:1
var ______________________________________________; throw 10
Expand Down

0 comments on commit d09cb2f

Please sign in to comment.