From 8acef08e8e1350fc30820772cacf1dfe78c1c3f9 Mon Sep 17 00:00:00 2001 From: Nigro Simone Date: Tue, 29 Sep 2026 15:59:58 +0200 Subject: [PATCH] src: cut small costs in node::MakeCallback Enter the Environment's context only when it is not already the entered and current one, set the async context frame only when it changes, pass the resource variant to push_async_context() by const reference, and grow and shrink the native resource stack with push_back() and pop_back() instead of resize(). Refs: https://github.com/nodejs/performance/issues/24 Signed-off-by: Nigro Simone --- src/api/callback.cc | 11 ++++++++++- src/async_context_frame.cc | 3 ++- src/env.cc | 16 ++++++++++++---- src/env.h | 2 +- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/api/callback.cc b/src/api/callback.cc index 217aebae9e16..b86e865466db 100644 --- a/src/api/callback.cc +++ b/src/api/callback.cc @@ -4,6 +4,8 @@ #include "node.h" #include "v8.h" +#include + namespace node { using v8::Context; @@ -349,7 +351,14 @@ MaybeLocal InternalMakeCallback(Isolate* isolate, } Environment* env = Environment::GetCurrent(context); CHECK_NOT_NULL(env); - Context::Scope context_scope(env->context()); + // Entering the context changes nothing when it is already the entered and + // the current one, which is the common case for addons. + Local env_context = env->context(); + std::optional context_scope; + if (isolate->GetEnteredOrMicrotaskContext() != env_context || + isolate->GetCurrentContext() != env_context) { + context_scope.emplace(env_context); + } MaybeLocal ret = InternalMakeCallback( env, recv, recv, callback, argc, argv, asyncContext, context_frame); if (ret.IsEmpty() && env->async_callback_scope_depth() == 0) { diff --git a/src/async_context_frame.cc b/src/async_context_frame.cc index 2e6379045733..7ea861a559e5 100644 --- a/src/async_context_frame.cc +++ b/src/async_context_frame.cc @@ -53,7 +53,8 @@ void set(Isolate* isolate, Local value) { // but sometimes (such as enterWith) a direct exchange is needed. Local exchange(Environment* env, Local value) { auto prior = current(env->isolate()); - set(env, value); + // Usually the frame does not change, and there is nothing to set. + if (prior != value) set(env, value); return prior; } diff --git a/src/env.cc b/src/env.cc index 1f8235598c85..7ec6c02cd655 100644 --- a/src/env.cc +++ b/src/env.cc @@ -133,7 +133,7 @@ void Environment::ResetPromiseHooks(Local init, void AsyncHooks::push_async_context( double async_id, double trigger_async_id, - std::variant*, Global*> resource) { + const std::variant*, Global*>& resource) { std::visit([](auto* ptr) { CHECK_IMPLIES(ptr != nullptr, !ptr->IsEmpty()); }, resource); @@ -161,9 +161,13 @@ void AsyncHooks::push_async_context( // False positive: https://github.com/cpplint/cpplint/issues/410 // NOLINTNEXTLINE(whitespace/newline) if (std::visit([](auto* ptr) { return ptr != nullptr; }, resource)) { - native_execution_async_resources_.resize(offset + 1); // Caveat: This is a v8::Local<>* assignment, we do not keep a v8::Global<>! - native_execution_async_resources_[offset] = resource; + if (native_execution_async_resources_.size() == offset) { + native_execution_async_resources_.push_back(resource); + } else { + native_execution_async_resources_.resize(offset + 1); + native_execution_async_resources_[offset] = resource; + } } } @@ -196,7 +200,11 @@ bool AsyncHooks::pop_async_context(double async_id) { native_execution_async_resources_[i]); } #endif - native_execution_async_resources_.resize(offset); + if (native_execution_async_resources_.size() == offset + 1) { + native_execution_async_resources_.pop_back(); + } else { + native_execution_async_resources_.resize(offset); + } native_execution_async_resources_.shrink_to_fit(); } diff --git a/src/env.h b/src/env.h index bbe417852e97..ad54cd1ea421 100644 --- a/src/env.h +++ b/src/env.h @@ -427,7 +427,7 @@ class AsyncHooks : public MemoryRetainer { void push_async_context( double async_id, double trigger_async_id, - std::variant*, v8::Global*> + const std::variant*, v8::Global*>& execution_async_resource); bool pop_async_context(double async_id); void clear_async_id_stack(); // Used in fatal exceptions.