From 4667f9512f45d286911488d3a199b2c82894e6b6 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 | 20 ++++++++++++++++---- src/env.cc | 16 ++++++++++++---- src/env.h | 2 +- 3 files changed, 29 insertions(+), 9 deletions(-) diff --git a/src/api/callback.cc b/src/api/callback.cc index 217aebae9e16..43cbdf40aa5a 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; @@ -112,9 +114,12 @@ InternalCallbackScope::InternalCallbackScope( isolate->SetIdle(false); - // The prior frame is usually undefined: no global handle then. - Local prior_context_frame = - async_context_frame::exchange(env, context_frame); + // The prior frame is usually undefined: no global handle then. Usually it + // is also the frame of the callback, and there is nothing to set. + Local prior_context_frame = async_context_frame::current(isolate); + if (prior_context_frame != context_frame) { + async_context_frame::set(env, context_frame); + } if (!prior_context_frame->IsUndefined()) { prior_context_frame_.Reset(isolate, prior_context_frame); } @@ -349,7 +354,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/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.