Skip to content

Commit 089bef0

Browse files
committed
timers: improve setImmediate() performance
This commit improves setImmediate() performance by moving the try-finally block that wraps callback execution into a separate function because currently v8 never tries to optimize functions that contain try-finally blocks. With this change, there is a ~20-40% improvement in the included setImmediate() depth benchmarks. The breadth benchmarks show a slight improvement. PR-URL: #4169 Reviewed-By: Minwoo Jung <jmwsoft@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
1 parent ad8257f commit 089bef0

File tree

5 files changed

+141
-19
lines changed

5 files changed

+141
-19
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const bench = common.createBenchmark(main, {
5+
millions: [5]
6+
});
7+
8+
function main(conf) {
9+
const N = +conf.millions * 1e6;
10+
11+
process.on('exit', function() {
12+
bench.end(N / 1e6);
13+
});
14+
15+
function cb1(arg1) {}
16+
function cb2(arg1, arg2) {}
17+
function cb3(arg1, arg2, arg3) {}
18+
19+
bench.start();
20+
for (let i = 0; i < N; i++) {
21+
if (i % 3 === 0)
22+
setImmediate(cb3, 512, true, null);
23+
else if (i % 2 === 0)
24+
setImmediate(cb2, false, 5.1);
25+
else
26+
setImmediate(cb1, 0);
27+
}
28+
}
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const bench = common.createBenchmark(main, {
5+
millions: [10]
6+
});
7+
8+
function main(conf) {
9+
const N = +conf.millions * 1e6;
10+
11+
process.on('exit', function() {
12+
bench.end(N / 1e6);
13+
});
14+
15+
function cb() {}
16+
17+
bench.start();
18+
for (let i = 0; i < N; i++) {
19+
setImmediate(cb);
20+
}
21+
}
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const bench = common.createBenchmark(main, {
5+
millions: [10]
6+
});
7+
8+
function main(conf) {
9+
const N = +conf.millions * 1e6;
10+
11+
process.on('exit', function() {
12+
bench.end(N / 1e6);
13+
});
14+
15+
function cb3(n, arg2, arg3) {
16+
if (--n) {
17+
if (n % 3 === 0)
18+
setImmediate(cb3, n, true, null);
19+
else if (n % 2 === 0)
20+
setImmediate(cb2, n, 5.1);
21+
else
22+
setImmediate(cb1, n);
23+
}
24+
}
25+
function cb2(n, arg2) {
26+
if (--n) {
27+
if (n % 3 === 0)
28+
setImmediate(cb3, n, true, null);
29+
else if (n % 2 === 0)
30+
setImmediate(cb2, n, 5.1);
31+
else
32+
setImmediate(cb1, n);
33+
}
34+
}
35+
function cb1(n) {
36+
if (--n) {
37+
if (n % 3 === 0)
38+
setImmediate(cb3, n, true, null);
39+
else if (n % 2 === 0)
40+
setImmediate(cb2, n, 5.1);
41+
else
42+
setImmediate(cb1, n);
43+
}
44+
}
45+
bench.start();
46+
setImmediate(cb1, N);
47+
}

benchmark/misc/set-immediate-depth.js

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

lib/timers.js

+23-19
Original file line numberDiff line numberDiff line change
@@ -215,8 +215,8 @@ function listOnTimeout() {
215215
}
216216

217217

218-
// An optimization so that the try/finally only de-optimizes what is in this
219-
// smaller function.
218+
// An optimization so that the try/finally only de-optimizes (since at least v8
219+
// 4.7) what is in this smaller function.
220220
function tryOnTimeout(timer, list) {
221221
timer._called = true;
222222
var threw = true;
@@ -520,23 +520,7 @@ function processImmediate() {
520520
if (domain)
521521
domain.enter();
522522

523-
var threw = true;
524-
try {
525-
immediate._onImmediate();
526-
threw = false;
527-
} finally {
528-
if (threw) {
529-
if (!L.isEmpty(queue)) {
530-
// Handle any remaining on next tick, assuming we're still
531-
// alive to do so.
532-
while (!L.isEmpty(immediateQueue)) {
533-
L.append(queue, L.shift(immediateQueue));
534-
}
535-
immediateQueue = queue;
536-
process.nextTick(processImmediate);
537-
}
538-
}
539-
}
523+
tryOnImmediate(immediate, queue);
540524

541525
if (domain)
542526
domain.exit();
@@ -551,6 +535,26 @@ function processImmediate() {
551535
}
552536

553537

538+
// An optimization so that the try/finally only de-optimizes (since at least v8
539+
// 4.7) what is in this smaller function.
540+
function tryOnImmediate(immediate, queue) {
541+
var threw = true;
542+
try {
543+
immediate._onImmediate();
544+
threw = false;
545+
} finally {
546+
if (threw && !L.isEmpty(queue)) {
547+
// Handle any remaining on next tick, assuming we're still alive to do so.
548+
while (!L.isEmpty(immediateQueue)) {
549+
L.append(queue, L.shift(immediateQueue));
550+
}
551+
immediateQueue = queue;
552+
process.nextTick(processImmediate);
553+
}
554+
}
555+
}
556+
557+
554558
function Immediate() { }
555559

556560
Immediate.prototype.domain = undefined;

0 commit comments

Comments
 (0)