Skip to content

Commit

Permalink
process: slightly simplify next tick execution
Browse files Browse the repository at this point in the history
Get rid of separate function to call callback from _tickCallback as
it no longer yields worthwhile performance improvement.

Move some code from nextTick & internalNextTick into TickObject
constructor to minimize duplication.
  • Loading branch information
apapirovski committed Nov 26, 2017
1 parent 0028241 commit c9513e1
Show file tree
Hide file tree
Showing 8 changed files with 81 additions and 53 deletions.
2 changes: 1 addition & 1 deletion benchmark/process/next-tick-breadth-args.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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

function main(conf) {
Expand Down
2 changes: 1 addition & 1 deletion benchmark/process/next-tick-breadth.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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

function main(conf) {
Expand Down
25 changes: 25 additions & 0 deletions benchmark/process/next-tick-exec-args.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
'use strict';
const common = require('../common.js');
const bench = common.createBenchmark(main, {
millions: [5]
});

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

bench.start();
for (var i = 0; i < n; i++) {
if (i % 4 === 0)
process.nextTick(onNextTick, i, true, 10, 'test');
else if (i % 3 === 0)
process.nextTick(onNextTick, i, true, 10);
else if (i % 2 === 0)
process.nextTick(onNextTick, i, 20);
else
process.nextTick(onNextTick, i);
}
function onNextTick(i) {
if (i + 1 === n)
bench.end(+conf.millions);
}
}
18 changes: 18 additions & 0 deletions benchmark/process/next-tick-exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
'use strict';
const common = require('../common.js');
const bench = common.createBenchmark(main, {
millions: [5]
});

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

bench.start();
for (var i = 0; i < n; i++) {
process.nextTick(onNextTick, i);
}
function onNextTick(i) {
if (i + 1 === n)
bench.end(+conf.millions);
}
}
77 changes: 33 additions & 44 deletions lib/internal/process/next_tick.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ function setupNextTick() {
// Grab the constants necessary for working with internal arrays.
const { kInit, kDestroy, kAsyncIdCounter } = async_wrap.constants;
const { async_id_symbol, trigger_async_id_symbol } = async_wrap;
var nextTickQueue = new NextTickQueue();
const nextTickQueue = new NextTickQueue();
var microtasksScheduled = false;

// Used to run V8's micro task queue.
Expand Down Expand Up @@ -99,7 +99,6 @@ function setupNextTick() {
const microTasksTickObject = {
callback: runMicrotasksCallback,
args: undefined,
domain: null,
[async_id_symbol]: 0,
[trigger_async_id_symbol]: 0
};
Expand All @@ -125,35 +124,13 @@ function setupNextTick() {
}
}

function _combinedTickCallback(args, callback) {
if (args === undefined) {
callback();
} else {
switch (args.length) {
case 1:
callback(args[0]);
break;
case 2:
callback(args[0], args[1]);
break;
case 3:
callback(args[0], args[1], args[2]);
break;
default:
callback(...args);
}
}
}

// Run callbacks that have no domain.
// Using domains will cause this to be overridden.
function _tickCallback() {
do {
while (tickInfo[kIndex] < tickInfo[kLength]) {
++tickInfo[kIndex];
const tock = nextTickQueue.shift();
const callback = tock.callback;
const args = tock.args;

// CHECK(Number.isSafeInteger(tock[async_id_symbol]))
// CHECK(tock[async_id_symbol] > 0)
Expand All @@ -173,10 +150,11 @@ function setupNextTick() {
if (async_hook_fields[kDestroy] > 0)
emitDestroy(tock[async_id_symbol]);

// Using separate callback execution functions allows direct
// callback invocation with small numbers of arguments to avoid the
// performance hit associated with using `fn.apply()`
_combinedTickCallback(args, callback);
const callback = tock.callback;
if (tock.args === undefined)
callback();
else
Reflect.apply(callback, undefined, tock.args);

emitAfter(tock[async_id_symbol]);

Expand All @@ -191,11 +169,21 @@ function setupNextTick() {

class TickObject {
constructor(callback, args, asyncId, triggerAsyncId) {
// this must be set to null first to avoid function tracking
// on the hidden class, revisit in V8 versions after 6.2
this.callback = null;
this.callback = callback;
this.args = args;
this.domain = process.domain || null;

this[async_id_symbol] = asyncId;
this[trigger_async_id_symbol] = triggerAsyncId;

if (async_hook_fields[kInit] > 0) {
emitInit(asyncId,
'TickObject',
triggerAsyncId,
this);
}
}
}

Expand All @@ -220,13 +208,14 @@ function setupNextTick() {
args[i - 1] = arguments[i];
}

const asyncId = ++async_id_fields[kAsyncIdCounter];
const triggerAsyncId = initTriggerId();
const obj = new TickObject(callback, args, asyncId, triggerAsyncId);
nextTickQueue.push(obj);
// In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the
// TickObject incurs a significant performance penalty in the
// next-tick-breadth-args benchmark (revisit later)
++tickInfo[kLength];
if (async_hook_fields[kInit] > 0)
emitInit(asyncId, 'TickObject', triggerAsyncId, obj);
nextTickQueue.push(new TickObject(callback,
args,
++async_id_fields[kAsyncIdCounter],
initTriggerId()));
}

// `internalNextTick()` will not enqueue any callback when the process is
Expand All @@ -240,10 +229,6 @@ function setupNextTick() {
if (process._exiting)
return;

if (triggerAsyncId === null) {
triggerAsyncId = async_hooks.initTriggerId();
}

var args;
switch (arguments.length) {
case 2: break;
Expand All @@ -256,11 +241,15 @@ function setupNextTick() {
args[i - 2] = arguments[i];
}

const asyncId = ++async_id_fields[kAsyncIdCounter];
const obj = new TickObject(callback, args, asyncId, triggerAsyncId);
nextTickQueue.push(obj);
if (triggerAsyncId === null)
triggerAsyncId = initTriggerId();
// In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the
// TickObject incurs a significant performance penalty in the
// next-tick-breadth-args benchmark (revisit later)
++tickInfo[kLength];
if (async_hook_fields[kInit] > 0)
emitInit(asyncId, 'TickObject', triggerAsyncId, obj);
nextTickQueue.push(new TickObject(callback,
args,
++async_id_fields[kAsyncIdCounter],
triggerAsyncId));
}
}
1 change: 0 additions & 1 deletion test/message/nexttick_throw.out
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
^
ReferenceError: undefined_reference_error_maker is not defined
at *test*message*nexttick_throw.js:*:*
at _combinedTickCallback (internal/process/next_tick.js:*:*)
at process._tickCallback (internal/process/next_tick.js:*:*)
at Function.Module.runMain (module.js:*:*)
at startup (bootstrap_node.js:*:*)
Expand Down
7 changes: 3 additions & 4 deletions test/message/stdin_messages.out
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ SyntaxError: Strict mode code may not include a with statement
at Socket.<anonymous> (bootstrap_node.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
at process._tickCallback (internal/process/next_tick.js:*:*)
42
42
Expand All @@ -29,7 +28,7 @@ Error: hello
at Socket.<anonymous> (bootstrap_node.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
at process._tickCallback (internal/process/next_tick.js:*:*)
[stdin]:1
throw new Error("hello")
^
Expand All @@ -44,7 +43,7 @@ Error: hello
at Socket.<anonymous> (bootstrap_node.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
at process._tickCallback (internal/process/next_tick.js:*:*)
100
[stdin]:1
var x = 100; y = x;
Expand All @@ -60,7 +59,7 @@ ReferenceError: y is not defined
at Socket.<anonymous> (bootstrap_node.js:*:*)
at Socket.emit (events.js:*:*)
at endReadableNT (_stream_readable.js:*:*)
at _combinedTickCallback (internal/process/next_tick.js:*:*)
at process._tickCallback (internal/process/next_tick.js:*:*)

[stdin]:1
var ______________________________________________; throw 10
Expand Down
2 changes: 0 additions & 2 deletions test/message/unhandled_promise_trace_warnings.out
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
at *
at *
at *
at *
(node:*) Error: This was rejected
at * (*test*message*unhandled_promise_trace_warnings.js:*)
at *
Expand All @@ -34,7 +33,6 @@
at *
at *
at *
at *
(node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
at getAsynchronousRejectionWarningObject (internal/process/promises.js:*)
at rejectionHandled (internal/process/promises.js:*)
Expand Down

0 comments on commit c9513e1

Please sign in to comment.