Skip to content
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

process: slightly simplify next tick execution #16888

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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