Skip to content

Commit 25650aa

Browse files
Anton Bikineevv8-scoped@luci-project-accounts.iam.gserviceaccount.com
authored andcommitted
[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 <[email protected]> Auto-Submit: Anton Bikineev <[email protected]> Reviewed-by: Michael Lippautz <[email protected]> Cr-Commit-Position: refs/heads/main@{#109567}
1 parent 2e9584b commit 25650aa

1 file changed

Lines changed: 20 additions & 2 deletions

File tree

‎src/execution/microtask-queue.cc‎

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "src/execution/isolate-inl.h"
2020
#include "src/handles/handle-scope-implementer-inl.h"
2121
#include "src/handles/handles-inl.h"
22+
#include "src/heap/heap-inl.h"
2223
#include "src/objects/microtask-inl.h"
2324
#include "src/objects/visitors.h"
2425
#include "src/roots/roots-inl.h"
@@ -172,6 +173,18 @@ bool MicrotaskQueue::ShouldPerformCheckpoint(v8::Isolate* v8_isolate) const {
172173

173174
void MicrotaskQueue::PerformCheckpointInternal(v8::Isolate* v8_isolate) {
174175
DCHECK(ShouldPerformCheckpoint(v8_isolate));
176+
DCHECK(!microtasks_completed_callbacks_cow_.has_value());
177+
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
178+
// Fast path: Checkpoints occur frequently when exiting script or microtask
179+
// scopes. If there are no microtasks to drain, no completion callbacks to
180+
// notify, and no kept objects from FinalizationRegistry / WeakRefs to clear,
181+
// we can bail out immediately and avoid artificial MicrotasksScope setup and
182+
// RunMicrotasks overhead.
183+
if (size() == 0 && microtasks_completed_callbacks_.empty() &&
184+
isolate->heap()->weak_refs_keep_during_job() ==
185+
ReadOnlyRoots(isolate).undefined_value()) [[likely]] {
186+
return;
187+
}
175188
std::optional<MicrotasksScope> microtasks_scope;
176189
if (microtasks_policy_ == v8::MicrotasksPolicy::kScoped) {
177190
// If we're using microtask scopes to schedule microtask execution, V8
@@ -182,7 +195,6 @@ void MicrotaskQueue::PerformCheckpointInternal(v8::Isolate* v8_isolate) {
182195
microtasks_scope.emplace(v8_isolate, this,
183196
v8::MicrotasksScope::kDoNotRunMicrotasks);
184197
}
185-
Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
186198
RunMicrotasks(isolate);
187199
isolate->ClearKeptObjects();
188200
}
@@ -355,12 +367,18 @@ void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
355367
}
356368

357369
void MicrotaskQueue::OnCompleted(Isolate* isolate) {
370+
DCHECK_IMPLIES(microtasks_completed_callbacks_.empty(),
371+
!microtasks_completed_callbacks_cow_.has_value());
372+
if (microtasks_completed_callbacks_.empty()) [[likely]] {
373+
return;
374+
}
375+
358376
is_running_completed_callbacks_ = true;
359377
for (auto& callback : microtasks_completed_callbacks_) {
360378
callback.first(reinterpret_cast<v8::Isolate*>(isolate), callback.second);
361379
}
362380
is_running_completed_callbacks_ = false;
363-
if (V8_UNLIKELY(microtasks_completed_callbacks_cow_.has_value())) {
381+
if (microtasks_completed_callbacks_cow_.has_value()) [[unlikely]] {
364382
microtasks_completed_callbacks_ =
365383
std::move(microtasks_completed_callbacks_cow_.value());
366384
microtasks_completed_callbacks_cow_.reset();

0 commit comments

Comments
 (0)