From 089bef0a811e3eba7a309da687e90499ec06ce63 Mon Sep 17 00:00:00 2001 From: Brian White Date: Sun, 6 Dec 2015 01:35:52 -0500 Subject: [PATCH] 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: https://github.com/nodejs/node/pull/4169 Reviewed-By: Minwoo Jung Reviewed-By: James M Snell Reviewed-By: Ben Noordhuis Reviewed-By: Jeremiah Senkpiel --- benchmark/misc/set-immediate-breadth-args.js | 28 ++++++++++++ benchmark/misc/set-immediate-breadth.js | 21 +++++++++ benchmark/misc/set-immediate-depth-args.js | 47 ++++++++++++++++++++ benchmark/misc/set-immediate-depth.js | 22 +++++++++ lib/timers.js | 42 +++++++++-------- 5 files changed, 141 insertions(+), 19 deletions(-) create mode 100644 benchmark/misc/set-immediate-breadth-args.js create mode 100644 benchmark/misc/set-immediate-breadth.js create mode 100644 benchmark/misc/set-immediate-depth-args.js create mode 100644 benchmark/misc/set-immediate-depth.js diff --git a/benchmark/misc/set-immediate-breadth-args.js b/benchmark/misc/set-immediate-breadth-args.js new file mode 100644 index 00000000000000..6a904e2675d110 --- /dev/null +++ b/benchmark/misc/set-immediate-breadth-args.js @@ -0,0 +1,28 @@ +'use strict'; + +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + millions: [5] +}); + +function main(conf) { + const N = +conf.millions * 1e6; + + process.on('exit', function() { + bench.end(N / 1e6); + }); + + function cb1(arg1) {} + function cb2(arg1, arg2) {} + function cb3(arg1, arg2, arg3) {} + + bench.start(); + for (let i = 0; i < N; i++) { + if (i % 3 === 0) + setImmediate(cb3, 512, true, null); + else if (i % 2 === 0) + setImmediate(cb2, false, 5.1); + else + setImmediate(cb1, 0); + } +} diff --git a/benchmark/misc/set-immediate-breadth.js b/benchmark/misc/set-immediate-breadth.js new file mode 100644 index 00000000000000..3d8b038342634d --- /dev/null +++ b/benchmark/misc/set-immediate-breadth.js @@ -0,0 +1,21 @@ +'use strict'; + +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + millions: [10] +}); + +function main(conf) { + const N = +conf.millions * 1e6; + + process.on('exit', function() { + bench.end(N / 1e6); + }); + + function cb() {} + + bench.start(); + for (let i = 0; i < N; i++) { + setImmediate(cb); + } +} diff --git a/benchmark/misc/set-immediate-depth-args.js b/benchmark/misc/set-immediate-depth-args.js new file mode 100644 index 00000000000000..1f12ae6ec73fc0 --- /dev/null +++ b/benchmark/misc/set-immediate-depth-args.js @@ -0,0 +1,47 @@ +'use strict'; + +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + millions: [10] +}); + +function main(conf) { + const N = +conf.millions * 1e6; + + process.on('exit', function() { + bench.end(N / 1e6); + }); + + function cb3(n, arg2, arg3) { + if (--n) { + if (n % 3 === 0) + setImmediate(cb3, n, true, null); + else if (n % 2 === 0) + setImmediate(cb2, n, 5.1); + else + setImmediate(cb1, n); + } + } + function cb2(n, arg2) { + if (--n) { + if (n % 3 === 0) + setImmediate(cb3, n, true, null); + else if (n % 2 === 0) + setImmediate(cb2, n, 5.1); + else + setImmediate(cb1, n); + } + } + function cb1(n) { + if (--n) { + if (n % 3 === 0) + setImmediate(cb3, n, true, null); + else if (n % 2 === 0) + setImmediate(cb2, n, 5.1); + else + setImmediate(cb1, n); + } + } + bench.start(); + setImmediate(cb1, N); +} diff --git a/benchmark/misc/set-immediate-depth.js b/benchmark/misc/set-immediate-depth.js new file mode 100644 index 00000000000000..12b9cdc7e6fd65 --- /dev/null +++ b/benchmark/misc/set-immediate-depth.js @@ -0,0 +1,22 @@ +'use strict'; + +const common = require('../common.js'); +const bench = common.createBenchmark(main, { + millions: [10] +}); + +function main(conf) { + const N = +conf.millions * 1e6; + let n = N; + + process.on('exit', function() { + bench.end(N / 1e6); + }); + + bench.start(); + setImmediate(onNextTick); + function onNextTick() { + if (--n) + setImmediate(onNextTick); + } +} diff --git a/lib/timers.js b/lib/timers.js index ee7c45aaed36d6..99010d424a984e 100644 --- a/lib/timers.js +++ b/lib/timers.js @@ -215,8 +215,8 @@ function listOnTimeout() { } -// An optimization so that the try/finally only de-optimizes what is in this -// smaller function. +// An optimization so that the try/finally only de-optimizes (since at least v8 +// 4.7) what is in this smaller function. function tryOnTimeout(timer, list) { timer._called = true; var threw = true; @@ -520,23 +520,7 @@ function processImmediate() { if (domain) domain.enter(); - var threw = true; - try { - immediate._onImmediate(); - threw = false; - } finally { - if (threw) { - if (!L.isEmpty(queue)) { - // Handle any remaining on next tick, assuming we're still - // alive to do so. - while (!L.isEmpty(immediateQueue)) { - L.append(queue, L.shift(immediateQueue)); - } - immediateQueue = queue; - process.nextTick(processImmediate); - } - } - } + tryOnImmediate(immediate, queue); if (domain) domain.exit(); @@ -551,6 +535,26 @@ function processImmediate() { } +// An optimization so that the try/finally only de-optimizes (since at least v8 +// 4.7) what is in this smaller function. +function tryOnImmediate(immediate, queue) { + var threw = true; + try { + immediate._onImmediate(); + threw = false; + } finally { + if (threw && !L.isEmpty(queue)) { + // Handle any remaining on next tick, assuming we're still alive to do so. + while (!L.isEmpty(immediateQueue)) { + L.append(queue, L.shift(immediateQueue)); + } + immediateQueue = queue; + process.nextTick(processImmediate); + } + } +} + + function Immediate() { } Immediate.prototype.domain = undefined;