From a1da3a8b71b1acbcf85025c211d5e20ceef9c681 Mon Sep 17 00:00:00 2001 From: Nigro Simone Date: Sun, 4 Oct 2026 07:57:04 +0200 Subject: [PATCH] deps: V8: cherry-pick 25650aa994 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Original commit message: [microtask] Early-exit empty PerformCheckpointInternal and OnCompleted Microtask checkpoints (MicrotaskQueue::PerformCheckpoint) are executed frequently at the end of every DOM event listener, microtask scope exit, timer callback, and mutation observer turn. In typical DOM interactions and event dispatch cycles, the microtask queue is frequently empty (size_ == 0), no microtask completion callbacks are registered, and no weak references were kept during the job (weak_refs_keep_during_job == undefined_value()). Previously, even when the queue was empty, PerformCheckpointInternal unconditionally allocated an artificial MicrotasksScope(kDoNotRunMicrotasks), called RunMicrotasks (which set running state flags, created suppress scopes, and rewound entered contexts), invoked OnCompleted, and called isolate->ClearKeptObjects() (performing an unconditional store to the weak_refs_keep_during_job root table slot). This CL fast-paths PerformCheckpointInternal when size() == 0, microtasks_completed_callbacks_ is empty, and weak_refs_keep_during_job is undefined. If WeakRef.prototype.deref() was invoked during the turn, weak_refs_keep_during_job holds an OrderedHashSet, so the early-exit is skipped and ClearKeptObjects() runs to clear kept objects as required by ECMAScript ยง26.1. Pinpoint Try Job on Apple Silicon M1 (150 iterations, Job 158c2910690000): - Speedometer 3 Score: 31.42 -> 31.48 (+0.21%, p=0.0293, stat-sig win) - TodoMVC-JavaScript-ES6-Webpack: -0.87% (p=0.0001) - NewsSite-Nuxt: -0.40% (p=0.0001) - TodoMVC-Angular: -0.45% (p=0.0681) - TodoMVC-jQuery: -0.36% (p=0.6023) - Charts-chartjs: -0.21% (p=0.0005) - React-Stockcharts-SVG: -0.26% (p=0.0144) Bug: 553846289 TAG=agy CONV=a3721850-14cb-425b-8d34-169f9934ed78 Change-Id: Ic996d283b746a5684a55c9fbc56d8fcfb38ac299 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/8312487 Commit-Queue: Anton Bikineev Auto-Submit: Anton Bikineev Reviewed-by: Michael Lippautz Cr-Commit-Position: refs/heads/main@{#109567} Refs: https://github.com/v8/v8/commit/25650aa99470f7c4a19336e71e3680d535e59896 Signed-off-by: Nigro Simone --- common.gypi | 2 +- deps/v8/src/execution/microtask-queue.cc | 22 ++++++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/common.gypi b/common.gypi index 841dc0288cd8..204098ee213c 100644 --- a/common.gypi +++ b/common.gypi @@ -44,7 +44,7 @@ # Reset this number to 0 on major V8 upgrades. # Increment by one for each non-official patch applied to deps/v8. - 'v8_embedder_string': '-node.37', + 'v8_embedder_string': '-node.38', ##### V8 defaults for Node.js ##### diff --git a/deps/v8/src/execution/microtask-queue.cc b/deps/v8/src/execution/microtask-queue.cc index 7f2377dbd8c7..c155425ee64f 100644 --- a/deps/v8/src/execution/microtask-queue.cc +++ b/deps/v8/src/execution/microtask-queue.cc @@ -12,6 +12,7 @@ #include "src/base/logging.h" #include "src/execution/isolate.h" #include "src/handles/handles-inl.h" +#include "src/heap/heap-inl.h" #include "src/objects/microtask-inl.h" #include "src/objects/visitors.h" #include "src/roots/roots-inl.h" @@ -115,6 +116,18 @@ void MicrotaskQueue::EnqueueMicrotask(Tagged microtask) { void MicrotaskQueue::PerformCheckpointInternal(v8::Isolate* v8_isolate) { DCHECK(ShouldPerfomCheckpoint()); + DCHECK(!microtasks_completed_callbacks_cow_.has_value()); + Isolate* isolate = reinterpret_cast(v8_isolate); + // Fast path: Checkpoints occur frequently when exiting script or microtask + // scopes. If there are no microtasks to drain, no completion callbacks to + // notify, and no kept objects from FinalizationRegistry / WeakRefs to clear, + // we can bail out immediately and avoid artificial MicrotasksScope setup and + // RunMicrotasks overhead. + if (size() == 0 && microtasks_completed_callbacks_.empty() && + isolate->heap()->weak_refs_keep_during_job() == + ReadOnlyRoots(isolate).undefined_value()) [[likely]] { + return; + } std::optional microtasks_scope; if (microtasks_policy_ == v8::MicrotasksPolicy::kScoped) { // If we're using microtask scopes to schedule microtask execution, V8 @@ -125,7 +138,6 @@ void MicrotaskQueue::PerformCheckpointInternal(v8::Isolate* v8_isolate) { microtasks_scope.emplace(v8_isolate, this, v8::MicrotasksScope::kDoNotRunMicrotasks); } - Isolate* isolate = reinterpret_cast(v8_isolate); RunMicrotasks(isolate); isolate->ClearKeptObjects(); } @@ -290,12 +302,18 @@ void MicrotaskQueue::RemoveMicrotasksCompletedCallback( } void MicrotaskQueue::OnCompleted(Isolate* isolate) { + DCHECK_IMPLIES(microtasks_completed_callbacks_.empty(), + !microtasks_completed_callbacks_cow_.has_value()); + if (microtasks_completed_callbacks_.empty()) [[likely]] { + return; + } + is_running_completed_callbacks_ = true; for (auto& callback : microtasks_completed_callbacks_) { callback.first(reinterpret_cast(isolate), callback.second); } is_running_completed_callbacks_ = false; - if (V8_UNLIKELY(microtasks_completed_callbacks_cow_.has_value())) { + if (microtasks_completed_callbacks_cow_.has_value()) [[unlikely]] { microtasks_completed_callbacks_ = std::move(microtasks_completed_callbacks_cow_.value()); microtasks_completed_callbacks_cow_.reset();