1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 08:33:43 -05:00

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

@ -138,6 +138,15 @@ fn op_is_any_arraybuffer(value: serde_v8::Value) -> bool {
value.v8_value.is_array_buffer() || value.v8_value.is_shared_array_buffer()
}
#[op(fast)]
fn op_node_is_promise_rejected(value: serde_v8::Value) -> bool {
let Ok(promise) = v8::Local::<v8::Promise>::try_from(value.v8_value) else {
return false;
};
promise.state() == v8::PromiseState::Rejected
}
deno_core::extension!(deno_node,
deps = [ deno_io, deno_fs ],
parameters = [P: NodePermissions],
@ -233,6 +242,7 @@ deno_core::extension!(deno_node,
ops::http::op_node_http_request<P>,
op_node_build_os,
op_is_any_arraybuffer,
op_node_is_promise_rejected,
ops::require::op_require_init_paths,
ops::require::op_require_node_module_paths<P>,
ops::require::op_require_proxy_path,

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();
}