Skip to content

Commit 9d65724

Browse files
apapirovskiMylesBorins
authored andcommitted
process: slightly simplify next tick execution
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. PR-URL: #16888 Reviewed-By: Anna Henningsen <[email protected]> Reviewed-By: Colin Ihrig <[email protected]> Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Luigi Pinca <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Ruben Bridgewater <[email protected]> Reviewed-By: Timothy Gu <[email protected]>
1 parent 5736dc4 commit 9d65724

8 files changed

+81
-53
lines changed

benchmark/process/next-tick-breadth-args.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const common = require('../common.js');
44
const bench = common.createBenchmark(main, {
5-
millions: [2]
5+
millions: [4]
66
});
77

88
function main(conf) {

benchmark/process/next-tick-breadth.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
const common = require('../common.js');
44
const bench = common.createBenchmark(main, {
5-
millions: [2]
5+
millions: [4]
66
});
77

88
function main(conf) {
+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
const bench = common.createBenchmark(main, {
4+
millions: [5]
5+
});
6+
7+
function main(conf) {
8+
var n = +conf.millions * 1e6;
9+
10+
bench.start();
11+
for (var i = 0; i < n; i++) {
12+
if (i % 4 === 0)
13+
process.nextTick(onNextTick, i, true, 10, 'test');
14+
else if (i % 3 === 0)
15+
process.nextTick(onNextTick, i, true, 10);
16+
else if (i % 2 === 0)
17+
process.nextTick(onNextTick, i, 20);
18+
else
19+
process.nextTick(onNextTick, i);
20+
}
21+
function onNextTick(i) {
22+
if (i + 1 === n)
23+
bench.end(+conf.millions);
24+
}
25+
}

benchmark/process/next-tick-exec.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict';
2+
const common = require('../common.js');
3+
const bench = common.createBenchmark(main, {
4+
millions: [5]
5+
});
6+
7+
function main(conf) {
8+
var n = +conf.millions * 1e6;
9+
10+
bench.start();
11+
for (var i = 0; i < n; i++) {
12+
process.nextTick(onNextTick, i);
13+
}
14+
function onNextTick(i) {
15+
if (i + 1 === n)
16+
bench.end(+conf.millions);
17+
}
18+
}

lib/internal/process/next_tick.js

+33-44
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ function setupNextTick() {
6060
// Grab the constants necessary for working with internal arrays.
6161
const { kInit, kDestroy, kAsyncIdCounter } = async_wrap.constants;
6262
const { async_id_symbol, trigger_async_id_symbol } = async_wrap;
63-
var nextTickQueue = new NextTickQueue();
63+
const nextTickQueue = new NextTickQueue();
6464
var microtasksScheduled = false;
6565

6666
// Used to run V8's micro task queue.
@@ -99,7 +99,6 @@ function setupNextTick() {
9999
const microTasksTickObject = {
100100
callback: runMicrotasksCallback,
101101
args: undefined,
102-
domain: null,
103102
[async_id_symbol]: 0,
104103
[trigger_async_id_symbol]: 0
105104
};
@@ -125,35 +124,13 @@ function setupNextTick() {
125124
}
126125
}
127126

128-
function _combinedTickCallback(args, callback) {
129-
if (args === undefined) {
130-
callback();
131-
} else {
132-
switch (args.length) {
133-
case 1:
134-
callback(args[0]);
135-
break;
136-
case 2:
137-
callback(args[0], args[1]);
138-
break;
139-
case 3:
140-
callback(args[0], args[1], args[2]);
141-
break;
142-
default:
143-
callback(...args);
144-
}
145-
}
146-
}
147-
148127
// Run callbacks that have no domain.
149128
// Using domains will cause this to be overridden.
150129
function _tickCallback() {
151130
do {
152131
while (tickInfo[kIndex] < tickInfo[kLength]) {
153132
++tickInfo[kIndex];
154133
const tock = nextTickQueue.shift();
155-
const callback = tock.callback;
156-
const args = tock.args;
157134

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

176-
// Using separate callback execution functions allows direct
177-
// callback invocation with small numbers of arguments to avoid the
178-
// performance hit associated with using `fn.apply()`
179-
_combinedTickCallback(args, callback);
153+
const callback = tock.callback;
154+
if (tock.args === undefined)
155+
callback();
156+
else
157+
Reflect.apply(callback, undefined, tock.args);
180158

181159
emitAfter(tock[async_id_symbol]);
182160

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

192170
class TickObject {
193171
constructor(callback, args, asyncId, triggerAsyncId) {
172+
// this must be set to null first to avoid function tracking
173+
// on the hidden class, revisit in V8 versions after 6.2
174+
this.callback = null;
194175
this.callback = callback;
195176
this.args = args;
196-
this.domain = process.domain || null;
177+
197178
this[async_id_symbol] = asyncId;
198179
this[trigger_async_id_symbol] = triggerAsyncId;
180+
181+
if (async_hook_fields[kInit] > 0) {
182+
emitInit(asyncId,
183+
'TickObject',
184+
triggerAsyncId,
185+
this);
186+
}
199187
}
200188
}
201189

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

223-
const asyncId = ++async_id_fields[kAsyncIdCounter];
224-
const triggerAsyncId = initTriggerId();
225-
const obj = new TickObject(callback, args, asyncId, triggerAsyncId);
226-
nextTickQueue.push(obj);
211+
// In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the
212+
// TickObject incurs a significant performance penalty in the
213+
// next-tick-breadth-args benchmark (revisit later)
227214
++tickInfo[kLength];
228-
if (async_hook_fields[kInit] > 0)
229-
emitInit(asyncId, 'TickObject', triggerAsyncId, obj);
215+
nextTickQueue.push(new TickObject(callback,
216+
args,
217+
++async_id_fields[kAsyncIdCounter],
218+
initTriggerId()));
230219
}
231220

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

243-
if (triggerAsyncId === null) {
244-
triggerAsyncId = async_hooks.initTriggerId();
245-
}
246-
247232
var args;
248233
switch (arguments.length) {
249234
case 2: break;
@@ -256,11 +241,15 @@ function setupNextTick() {
256241
args[i - 2] = arguments[i];
257242
}
258243

259-
const asyncId = ++async_id_fields[kAsyncIdCounter];
260-
const obj = new TickObject(callback, args, asyncId, triggerAsyncId);
261-
nextTickQueue.push(obj);
244+
if (triggerAsyncId === null)
245+
triggerAsyncId = initTriggerId();
246+
// In V8 6.2, moving tickInfo & async_id_fields[kAsyncIdCounter] into the
247+
// TickObject incurs a significant performance penalty in the
248+
// next-tick-breadth-args benchmark (revisit later)
262249
++tickInfo[kLength];
263-
if (async_hook_fields[kInit] > 0)
264-
emitInit(asyncId, 'TickObject', triggerAsyncId, obj);
250+
nextTickQueue.push(new TickObject(callback,
251+
args,
252+
++async_id_fields[kAsyncIdCounter],
253+
triggerAsyncId));
265254
}
266255
}

test/message/nexttick_throw.out

-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
^
55
ReferenceError: undefined_reference_error_maker is not defined
66
at *test*message*nexttick_throw.js:*:*
7-
at _combinedTickCallback (internal/process/next_tick.js:*:*)
87
at process._tickCallback (internal/process/next_tick.js:*:*)
98
at Function.Module.runMain (module.js:*:*)
109
at startup (bootstrap_node.js:*:*)

test/message/stdin_messages.out

+3-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ SyntaxError: Strict mode code may not include a with statement
1111
at Socket.<anonymous> (bootstrap_node.js:*:*)
1212
at Socket.emit (events.js:*:*)
1313
at endReadableNT (_stream_readable.js:*:*)
14-
at _combinedTickCallback (internal/process/next_tick.js:*:*)
1514
at process._tickCallback (internal/process/next_tick.js:*:*)
1615
42
1716
42
@@ -29,7 +28,7 @@ Error: hello
2928
at Socket.<anonymous> (bootstrap_node.js:*:*)
3029
at Socket.emit (events.js:*:*)
3130
at endReadableNT (_stream_readable.js:*:*)
32-
at _combinedTickCallback (internal/process/next_tick.js:*:*)
31+
at process._tickCallback (internal/process/next_tick.js:*:*)
3332
[stdin]:1
3433
throw new Error("hello")
3534
^
@@ -44,7 +43,7 @@ Error: hello
4443
at Socket.<anonymous> (bootstrap_node.js:*:*)
4544
at Socket.emit (events.js:*:*)
4645
at endReadableNT (_stream_readable.js:*:*)
47-
at _combinedTickCallback (internal/process/next_tick.js:*:*)
46+
at process._tickCallback (internal/process/next_tick.js:*:*)
4847
100
4948
[stdin]:1
5049
var x = 100; y = x;
@@ -60,7 +59,7 @@ ReferenceError: y is not defined
6059
at Socket.<anonymous> (bootstrap_node.js:*:*)
6160
at Socket.emit (events.js:*:*)
6261
at endReadableNT (_stream_readable.js:*:*)
63-
at _combinedTickCallback (internal/process/next_tick.js:*:*)
62+
at process._tickCallback (internal/process/next_tick.js:*:*)
6463

6564
[stdin]:1
6665
var ______________________________________________; throw 10

test/message/unhandled_promise_trace_warnings.out

-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515
at *
1616
at *
1717
at *
18-
at *
1918
(node:*) Error: This was rejected
2019
at * (*test*message*unhandled_promise_trace_warnings.js:*)
2120
at *
@@ -34,7 +33,6 @@
3433
at *
3534
at *
3635
at *
37-
at *
3836
(node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
3937
at getAsynchronousRejectionWarningObject (internal/process/promises.js:*)
4038
at rejectionHandled (internal/process/promises.js:*)

0 commit comments

Comments
 (0)