async_hooks: optimize fast-path promise hook for ALS

Remove unnecessary native-to-JS code switches in fast-path for
PromiseHooks. Those switches happen even if a certain type of
hook (say, before) is not installed, which may lead to sub-optimal
performance in the AsyncLocalStorage scenario, i.e. when there is
only an init hook.

PR-URL: https://github.com/nodejs/node/pull/34512
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: David Carlier <devnexen@gmail.com>
This commit is contained in:
Andrey Pechkurov
2020-07-25 20:37:30 +03:00
parent 6fd09e4f36
commit 53870ddeac
2 changed files with 65 additions and 7 deletions

View File

@@ -19,6 +19,11 @@ const tests = {
promiseResolve() {},
destroy() {}
}).enable();
},
enabledWithInitOnly() {
hook = createHook({
init() {}
}).enable();
}
};
@@ -27,6 +32,7 @@ const bench = common.createBenchmark(main, {
asyncHooks: [
'enabled',
'enabledWithDestroy',
'enabledWithInitOnly',
'disabled',
]
});

View File

@@ -231,19 +231,18 @@ PromiseWrap* PromiseWrap::New(Environment* env,
// Skip for init events
if (silent) {
Local<Value> maybeAsyncId = promise
Local<Value> maybe_async_id = promise
->Get(context, env->async_id_symbol())
.ToLocalChecked();
Local<Value> maybeTriggerAsyncId = promise
Local<Value> maybe_trigger_async_id = promise
->Get(context, env->trigger_async_id_symbol())
.ToLocalChecked();
if (maybeAsyncId->IsNumber() && maybeTriggerAsyncId->IsNumber()) {
double asyncId = maybeAsyncId->NumberValue(context).ToChecked();
double triggerAsyncId = maybeTriggerAsyncId->NumberValue(context)
.ToChecked();
return new PromiseWrap(env, obj, asyncId, triggerAsyncId);
if (maybe_async_id->IsNumber() && maybe_trigger_async_id->IsNumber()) {
double async_id = maybe_async_id.As<Number>()->Value();
double trigger_async_id = maybe_trigger_async_id.As<Number>()->Value();
return new PromiseWrap(env, obj, async_id, trigger_async_id);
}
}
@@ -320,6 +319,59 @@ static void FastPromiseHook(PromiseHookType type, Local<Promise> promise,
Environment* env = Environment::GetCurrent(context);
if (env == nullptr) return;
if (type == PromiseHookType::kBefore &&
env->async_hooks()->fields()[AsyncHooks::kBefore] == 0) {
Local<Value> maybe_async_id;
if (!promise->Get(context, env->async_id_symbol())
.ToLocal(&maybe_async_id)) {
return;
}
Local<Value> maybe_trigger_async_id;
if (!promise->Get(context, env->trigger_async_id_symbol())
.ToLocal(&maybe_trigger_async_id)) {
return;
}
if (maybe_async_id->IsNumber() && maybe_trigger_async_id->IsNumber()) {
double async_id = maybe_async_id.As<Number>()->Value();
double trigger_async_id = maybe_trigger_async_id.As<Number>()->Value();
env->async_hooks()->push_async_context(
async_id, trigger_async_id, promise);
}
return;
}
if (type == PromiseHookType::kAfter &&
env->async_hooks()->fields()[AsyncHooks::kAfter] == 0) {
Local<Value> maybe_async_id;
if (!promise->Get(context, env->async_id_symbol())
.ToLocal(&maybe_async_id)) {
return;
}
if (maybe_async_id->IsNumber()) {
double async_id = maybe_async_id.As<Number>()->Value();
if (env->execution_async_id() == async_id) {
// This condition might not be true if async_hooks was enabled during
// the promise callback execution.
env->async_hooks()->pop_async_context(async_id);
}
}
return;
}
if (type == PromiseHookType::kResolve &&
env->async_hooks()->fields()[AsyncHooks::kPromiseResolve] == 0) {
return;
}
// Getting up to this point means either init type or
// that there are active hooks of another type.
// In both cases fast-path JS hook should be called.
Local<Value> argv[] = {
Integer::New(env->isolate(), ToAsyncHooksType(type)),
promise,