diff options
author | Anna Henningsen <anna@addaleax.net> | 2017-12-19 15:08:18 +0100 |
---|---|---|
committer | Anna Henningsen <anna@addaleax.net> | 2017-12-27 19:48:07 +0100 |
commit | 83e5215a4e8438a43b9f0002b7a43e2fd2dd37a4 (patch) | |
tree | 3a0be3dda02df06814d9ec9b73a22c1afa4b0ddc /lib | |
parent | df30fd586d123fc189887fc0f9c7f27808374c2e (diff) | |
download | android-node-v8-83e5215a4e8438a43b9f0002b7a43e2fd2dd37a4.tar.gz android-node-v8-83e5215a4e8438a43b9f0002b7a43e2fd2dd37a4.tar.bz2 android-node-v8-83e5215a4e8438a43b9f0002b7a43e2fd2dd37a4.zip |
async_hooks: use typed array stack as fast path
- Communicate the current async stack length through a
typed array field rather than a native binding method
- Add a new fixed-size `async_ids_fast_stack` typed array
that contains the async ID stack up to a fixed limit.
This increases performance noticeably, since most of the time
the async ID stack will not be more than a handful of
levels deep.
- Make the JS `pushAsyncIds()` and `popAsyncIds()` functions
do the same thing as the native ones if the fast path
is applicable.
Benchmarks:
$ ./node benchmark/compare.js --new ./node --old ./node-master --runs 10 --filter next-tick process | Rscript benchmark/compare.R
[00:03:25|% 100| 6/6 files | 20/20 runs | 1/1 configs]: Done
improvement confidence p.value
process/next-tick-breadth-args.js millions=4 19.72 % *** 3.013913e-06
process/next-tick-breadth.js millions=4 27.33 % *** 5.847983e-11
process/next-tick-depth-args.js millions=12 40.08 % *** 1.237127e-13
process/next-tick-depth.js millions=12 77.27 % *** 1.413290e-11
process/next-tick-exec-args.js millions=5 13.58 % *** 1.245180e-07
process/next-tick-exec.js millions=5 16.80 % *** 2.961386e-07
PR-URL: https://github.com/nodejs/node/pull/17780
Reviewed-By: James M Snell <jasnell@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/internal/async_hooks.js | 44 | ||||
-rw-r--r-- | lib/internal/bootstrap_node.js | 6 |
2 files changed, 44 insertions, 6 deletions
diff --git a/lib/internal/async_hooks.js b/lib/internal/async_hooks.js index 035dd0634d..7d99985dd1 100644 --- a/lib/internal/async_hooks.js +++ b/lib/internal/async_hooks.js @@ -19,6 +19,12 @@ const async_wrap = process.binding('async_wrap'); * retrieving the triggerAsyncId value is passing directly to the * constructor -> value set in kDefaultTriggerAsyncId -> executionAsyncId of * the current resource. + * + * async_ids_fast_stack is a Float64Array that contains part of the async ID + * stack. Each pushAsyncIds() call adds two doubles to it, and each + * popAsyncIds() call removes two doubles from it. + * It has a fixed size, so if that is exceeded, calls to the native + * side are used instead in pushAsyncIds() and popAsyncIds(). */ const { async_hook_fields, async_id_fields } = async_wrap; // Store the pair executionAsyncId and triggerAsyncId in a std::stack on @@ -26,7 +32,7 @@ const { async_hook_fields, async_id_fields } = async_wrap; // current execution stack. This is unwound as each resource exits. In the case // of a fatal exception this stack is emptied after calling each hook's after() // callback. -const { pushAsyncIds, popAsyncIds } = async_wrap; +const { pushAsyncIds: pushAsyncIds_, popAsyncIds: popAsyncIds_ } = async_wrap; // For performance reasons, only track Proimses when a hook is enabled. const { enablePromiseHook, disablePromiseHook } = async_wrap; // Properties in active_hooks are used to keep track of the set of hooks being @@ -60,8 +66,8 @@ const active_hooks = { // async execution. These are tracked so if the user didn't include callbacks // for a given step, that step can bail out early. const { kInit, kBefore, kAfter, kDestroy, kPromiseResolve, - kCheck, kExecutionAsyncId, kAsyncIdCounter, - kDefaultTriggerAsyncId } = async_wrap.constants; + kCheck, kExecutionAsyncId, kAsyncIdCounter, kTriggerAsyncId, + kDefaultTriggerAsyncId, kStackLength } = async_wrap.constants; // Used in AsyncHook and AsyncResource. const init_symbol = Symbol('init'); @@ -329,6 +335,38 @@ function emitDestroyScript(asyncId) { } +// This is the equivalent of the native push_async_ids() call. +function pushAsyncIds(asyncId, triggerAsyncId) { + const offset = async_hook_fields[kStackLength]; + if (offset * 2 >= async_wrap.async_ids_stack.length) + return pushAsyncIds_(asyncId, triggerAsyncId); + async_wrap.async_ids_stack[offset * 2] = async_id_fields[kExecutionAsyncId]; + async_wrap.async_ids_stack[offset * 2 + 1] = async_id_fields[kTriggerAsyncId]; + async_hook_fields[kStackLength]++; + async_id_fields[kExecutionAsyncId] = asyncId; + async_id_fields[kTriggerAsyncId] = triggerAsyncId; +} + + +// This is the equivalent of the native pop_async_ids() call. +function popAsyncIds(asyncId) { + if (async_hook_fields[kStackLength] === 0) return false; + const stackLength = async_hook_fields[kStackLength]; + + if (async_hook_fields[kCheck] > 0 && + async_id_fields[kExecutionAsyncId] !== asyncId) { + // Do the same thing as the native code (i.e. crash hard). + return popAsyncIds_(asyncId); + } + + const offset = stackLength - 1; + async_id_fields[kExecutionAsyncId] = async_wrap.async_ids_stack[2 * offset]; + async_id_fields[kTriggerAsyncId] = async_wrap.async_ids_stack[2 * offset + 1]; + async_hook_fields[kStackLength] = offset; + return offset > 0; +} + + module.exports = { // Private API getHookArrays, diff --git a/lib/internal/bootstrap_node.js b/lib/internal/bootstrap_node.js index f8a1fa3961..298fc9df30 100644 --- a/lib/internal/bootstrap_node.js +++ b/lib/internal/bootstrap_node.js @@ -367,9 +367,9 @@ // Arrays containing hook flags and ids for async_hook calls. const { async_hook_fields, async_id_fields } = async_wrap; // Internal functions needed to manipulate the stack. - const { clearAsyncIdStack, asyncIdStackSize } = async_wrap; + const { clearAsyncIdStack } = async_wrap; const { kAfter, kExecutionAsyncId, - kDefaultTriggerAsyncId } = async_wrap.constants; + kDefaultTriggerAsyncId, kStackLength } = async_wrap.constants; process._fatalException = function(er) { var caught; @@ -407,7 +407,7 @@ do { NativeModule.require('internal/async_hooks').emitAfter( async_id_fields[kExecutionAsyncId]); - } while (asyncIdStackSize() > 0); + } while (async_hook_fields[kStackLength] > 0); // Or completely empty the id stack. } else { clearAsyncIdStack(); |