-
Notifications
You must be signed in to change notification settings - Fork 29.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
node: improve nextTick performance #1571
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
'use strict'; | ||
|
||
var common = require('../common.js'); | ||
var bench = common.createBenchmark(main, { | ||
millions: [2] | ||
}); | ||
|
||
function main(conf) { | ||
var N = +conf.millions * 1e6; | ||
var n = 0; | ||
|
||
function cb1(arg1) { | ||
n++; | ||
if (n === N) | ||
bench.end(n / 1e6); | ||
} | ||
function cb2(arg1, arg2) { | ||
n++; | ||
if (n === N) | ||
bench.end(n / 1e6); | ||
} | ||
function cb3(arg1, arg2, arg3) { | ||
n++; | ||
if (n === N) | ||
bench.end(n / 1e6); | ||
} | ||
|
||
bench.start(); | ||
for (var i = 0; i < N; i++) { | ||
if (i % 3 === 0) | ||
process.nextTick(cb3, 512, true, null); | ||
else if (i % 2 === 0) | ||
process.nextTick(cb2, false, 5.1); | ||
else | ||
process.nextTick(cb1, 0); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. might add a case for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So I should merge the pre-existing non-args benchmark with this one? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
'use strict'; | ||
|
||
var common = require('../common.js'); | ||
var bench = common.createBenchmark(main, { | ||
millions: [2] | ||
}); | ||
|
||
process.maxTickDepth = Infinity; | ||
|
||
function main(conf) { | ||
var n = +conf.millions * 1e6; | ||
|
||
function cb3(arg1, arg2, arg3) { | ||
if (--n) { | ||
if (n % 3 === 0) | ||
process.nextTick(cb3, 512, true, null); | ||
else if (n % 2 === 0) | ||
process.nextTick(cb2, false, 5.1); | ||
else | ||
process.nextTick(cb1, 0); | ||
} else | ||
bench.end(+conf.millions); | ||
} | ||
function cb2(arg1, arg2) { | ||
if (--n) { | ||
if (n % 3 === 0) | ||
process.nextTick(cb3, 512, true, null); | ||
else if (n % 2 === 0) | ||
process.nextTick(cb2, false, 5.1); | ||
else | ||
process.nextTick(cb1, 0); | ||
} else | ||
bench.end(+conf.millions); | ||
} | ||
function cb1(arg1) { | ||
if (--n) { | ||
if (n % 3 === 0) | ||
process.nextTick(cb3, 512, true, null); | ||
else if (n % 2 === 0) | ||
process.nextTick(cb2, false, 5.1); | ||
else | ||
process.nextTick(cb1, 0); | ||
} else | ||
bench.end(+conf.millions); | ||
} | ||
bench.start(); | ||
process.nextTick(cb1, true); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -328,22 +328,33 @@ | |
// Run callbacks that have no domain. | ||
// Using domains will cause this to be overridden. | ||
function _tickCallback() { | ||
var callback, threw, tock; | ||
var callback, args, 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. a nit: would you mind adding a comment briefly explaining why this call is separated into a switch statement, for future readers? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
// Using separate callback execution functions helps to limit the | ||
// scope of DEOPTs caused by using try blocks and allows direct | ||
// callback invocation with small numbers of arguments to avoid the | ||
// performance hit associated with using `fn.apply()` | ||
if (args === undefined) { | ||
doNTCallback0(callback); | ||
} else { | ||
switch (args.length) { | ||
case 1: | ||
doNTCallback1(callback, args[0]); | ||
break; | ||
case 2: | ||
doNTCallback2(callback, args[0], args[1]); | ||
break; | ||
case 3: | ||
doNTCallback3(callback, args[0], args[1], args[2]); | ||
break; | ||
default: | ||
doNTCallbackMany(callback, args); | ||
} | ||
} | ||
if (1e4 < tickInfo[kIndex]) | ||
tickDone(); | ||
|
@@ -355,25 +366,36 @@ | |
} | ||
|
||
function _tickDomainCallback() { | ||
var callback, domain, threw, tock; | ||
var callback, domain, args, 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(); | ||
// Using separate callback execution functions helps to limit the | ||
// scope of DEOPTs caused by using try blocks and allows direct | ||
// callback invocation with small numbers of arguments to avoid the | ||
// performance hit associated with using `fn.apply()` | ||
if (args === undefined) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
doNTCallback0(callback); | ||
} else { | ||
switch (args.length) { | ||
case 1: | ||
doNTCallback1(callback, args[0]); | ||
break; | ||
case 2: | ||
doNTCallback2(callback, args[0], args[1]); | ||
break; | ||
case 3: | ||
doNTCallback3(callback, args[0], args[1], args[2]); | ||
break; | ||
default: | ||
doNTCallbackMany(callback, args); | ||
} | ||
} | ||
if (1e4 < tickInfo[kIndex]) | ||
tickDone(); | ||
|
@@ -386,6 +408,61 @@ | |
} while (tickInfo[kLength] !== 0); | ||
} | ||
|
||
function doNTCallback0(callback) { | ||
var threw = true; | ||
try { | ||
callback(); | ||
threw = false; | ||
} finally { | ||
if (threw) | ||
tickDone(); | ||
} | ||
} | ||
|
||
function doNTCallback1(callback, arg1) { | ||
var threw = true; | ||
try { | ||
callback(arg1); | ||
threw = false; | ||
} finally { | ||
if (threw) | ||
tickDone(); | ||
} | ||
} | ||
|
||
function doNTCallback2(callback, arg1, arg2) { | ||
var threw = true; | ||
try { | ||
callback(arg1, arg2); | ||
threw = false; | ||
} finally { | ||
if (threw) | ||
tickDone(); | ||
} | ||
} | ||
|
||
function doNTCallback3(callback, arg1, arg2, arg3) { | ||
var threw = true; | ||
try { | ||
callback(arg1, arg2, arg3); | ||
threw = false; | ||
} finally { | ||
if (threw) | ||
tickDone(); | ||
} | ||
} | ||
|
||
function doNTCallbackMany(callback, args) { | ||
var threw = true; | ||
try { | ||
callback.apply(null, args); | ||
threw = false; | ||
} finally { | ||
if (threw) | ||
tickDone(); | ||
} | ||
} | ||
|
||
function TickObject(c, args) { | ||
this.callback = c; | ||
this.domain = process.domain || null; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
'use strict'
?