summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2017-12-19 15:08:18 +0100
committerAnna Henningsen <anna@addaleax.net>2017-12-27 19:48:07 +0100
commit83e5215a4e8438a43b9f0002b7a43e2fd2dd37a4 (patch)
tree3a0be3dda02df06814d9ec9b73a22c1afa4b0ddc /lib
parentdf30fd586d123fc189887fc0f9c7f27808374c2e (diff)
downloadandroid-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.js44
-rw-r--r--lib/internal/bootstrap_node.js6
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();