perf(node/async_hooks): optimize AsyncLocalStorage (#19729)

This makes the implementation of "AsyncLocalStorage" from
"node:async_hooks" 3.5x faster than before for noop benchmark
(measuring baseline overhead). It's still 3.5x slower than not
using `AsyncLocalStorage` and 1.64x slower than using
noop promise hooks.
This commit is contained in:
Bartek Iwańczuk 2023-07-06 13:05:10 +02:00 committed by GitHub
parent 57fae55d82
commit de630b9b78
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 19 additions and 11 deletions

View file

@ -10,6 +10,7 @@
import { validateFunction } from "ext:deno_node/internal/validators.mjs";
const { core } = globalThis.__bootstrap;
const { ops } = core;
function assert(cond: boolean) {
if (!cond) throw new Error("Assertion failed");
@ -29,10 +30,6 @@ let rootAsyncFrame: AsyncContextFrame | undefined = undefined;
let promiseHooksSet = false;
const asyncContext = Symbol("asyncContext");
function isRejected(promise: Promise<unknown>) {
const [state] = core.getPromiseDetails(promise);
return state == 2;
}
function setPromiseHooks() {
if (promiseHooksSet) {
@ -43,12 +40,14 @@ function setPromiseHooks() {
const init = (promise: Promise<unknown>) => {
const currentFrame = AsyncContextFrame.current();
if (!currentFrame.isRoot()) {
assert(AsyncContextFrame.tryGetContext(promise) == null);
if (typeof promise[asyncContext] !== "undefined") {
throw new Error("Promise already has async context");
}
AsyncContextFrame.attachContext(promise);
}
};
const before = (promise: Promise<unknown>) => {
const maybeFrame = AsyncContextFrame.tryGetContext(promise);
const maybeFrame = promise[asyncContext];
if (maybeFrame) {
pushAsyncFrame(maybeFrame);
} else {
@ -57,16 +56,16 @@ function setPromiseHooks() {
};
const after = (promise: Promise<unknown>) => {
popAsyncFrame();
if (!isRejected(promise)) {
if (!ops.op_node_is_promise_rejected(promise)) {
// @ts-ignore promise async context
delete promise[asyncContext];
promise[asyncContext] = undefined;
}
};
const resolve = (promise: Promise<unknown>) => {
const currentFrame = AsyncContextFrame.current();
if (
!currentFrame.isRoot() && isRejected(promise) &&
AsyncContextFrame.tryGetContext(promise) == null
!currentFrame.isRoot() && ops.op_node_is_promise_rejected(promise) &&
typeof promise[asyncContext] === "undefined"
) {
AsyncContextFrame.attachContext(promise);
}
@ -117,7 +116,6 @@ class AsyncContextFrame {
}
static attachContext(promise: Promise<unknown>) {
assert(!(asyncContext in promise));
// @ts-ignore promise async context
promise[asyncContext] = AsyncContextFrame.current();
}