Commit 5a566ac6b97 for nodejs
commit 5a566ac6b971a9bcbcff1d338f391e0ae6dbd678
Author: Nigro Simone <nigro.simone@gmail.com>
Date: Tue Oct 20 16:24:33 2026 +0200
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 <nigro.simone@gmail.com>
PR-URL: https://github.com/nodejs/node/pull/66395
Reviewed-By: Xuguang Mei <meixuguang@gmail.com>
Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
diff --git a/src/api/callback.cc b/src/api/callback.cc
index 15bba45d786..7f74e011ff6 100644
--- a/src/api/callback.cc
+++ b/src/api/callback.cc
@@ -4,6 +4,8 @@
#include "node.h"
#include "v8.h"
+#include <optional>
+
namespace node {
using v8::Context;
@@ -348,7 +350,14 @@ MaybeLocal<Value> 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<Context> env_context = env->context();
+ std::optional<Context::Scope> context_scope;
+ if (isolate->GetEnteredOrMicrotaskContext() != env_context ||
+ isolate->GetCurrentContext() != env_context) {
+ context_scope.emplace(env_context);
+ }
MaybeLocal<Value> 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 b2b6514d9a2..3221d749fda 100644
--- a/src/async_context_frame.cc
+++ b/src/async_context_frame.cc
@@ -49,7 +49,8 @@ void set(Isolate* isolate, Local<Value> value) {
// but sometimes (such as enterWith) a direct exchange is needed.
Local<Value> exchange(Environment* env, Local<Value> 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 2da45b82f1a..d5b3b466f76 100644
--- a/src/env.cc
+++ b/src/env.cc
@@ -133,7 +133,7 @@ void Environment::ResetPromiseHooks(Local<Function> init,
void AsyncHooks::push_async_context(
double async_id,
double trigger_async_id,
- std::variant<Local<Object>*, Global<Object>*> resource) {
+ const std::variant<Local<Object>*, Global<Object>*>& 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 0349add7544..96b0cf52fb2 100644
--- a/src/env.h
+++ b/src/env.h
@@ -433,7 +433,7 @@ class AsyncHooks : public MemoryRetainer {
void push_async_context(
double async_id,
double trigger_async_id,
- std::variant<v8::Local<v8::Object>*, v8::Global<v8::Object>*>
+ const std::variant<v8::Local<v8::Object>*, v8::Global<v8::Object>*>&
execution_async_resource);
bool pop_async_context(double async_id);
void clear_async_id_stack(); // Used in fatal exceptions.