diff --git a/dom/base/CCGCScheduler.cpp b/dom/base/CCGCScheduler.cpp index dbb7db657859..83ab3d7c55ee 100644 --- a/dom/base/CCGCScheduler.cpp +++ b/dom/base/CCGCScheduler.cpp @@ -241,6 +241,10 @@ void CCGCScheduler::NoteCCEnd(const CycleCollectorResults& aResults, mIsCollectingCycles = false; mLastCCEndTime = aWhen; mNeedsFullCC = CCReason::NO_REASON; + mPreferFasterCollection = + mCurrentCollectionHasSeenNonIdle && + (aResults.mFreedGCed > 10000 || aResults.mFreedRefCounted > 10000); + mCurrentCollectionHasSeenNonIdle = false; } void CCGCScheduler::NoteWontGC() { @@ -255,6 +259,14 @@ void CCGCScheduler::NoteWontGC() { bool CCGCScheduler::GCRunnerFired(TimeStamp aDeadline) { MOZ_ASSERT(!mDidShutdown, "GCRunner still alive during shutdown"); + if (!aDeadline) { + mCurrentCollectionHasSeenNonIdle = true; + } else if (mPreferFasterCollection) { + // We found some idle time, try to utilize that a bit more given that + // we're in a mode where idle time is rare. + aDeadline = aDeadline + TimeDuration::FromMilliseconds(5.0); + } + GCRunnerStep step = GetNextGCRunnerAction(aDeadline); switch (step.mAction) { case GCRunnerAction::None: @@ -544,15 +556,24 @@ void CCGCScheduler::EnsureOrResetGCRunner() { StaticPrefs::javascript_options_gc_delay_interslice())); } +TimeDuration CCGCScheduler::ComputeMinimumBudgetForRunner( + TimeDuration aBaseValue) { + // If the main thread was too busy to find idle for the whole last collection, + // allow a very short budget this time. + return mPreferFasterCollection ? TimeDuration::FromMilliseconds(1.0) + : TimeDuration::FromMilliseconds(std::max( + nsRefreshDriver::HighRateMultiplier() * + aBaseValue.ToMilliseconds(), + 1.0)); +} + void CCGCScheduler::EnsureGCRunner(TimeDuration aDelay) { if (mGCRunner) { return; } - TimeDuration minimumBudget = TimeDuration::FromMilliseconds( - std::max(nsRefreshDriver::HighRateMultiplier() * - mActiveIntersliceGCBudget.ToMilliseconds(), - 1.0)); + TimeDuration minimumBudget = + ComputeMinimumBudgetForRunner(mActiveIntersliceGCBudget); // Wait at most the interslice GC delay before forcing a run. mGCRunner = IdleTaskRunner::Create( @@ -615,13 +636,13 @@ void CCGCScheduler::KillGCRunner() { void CCGCScheduler::EnsureCCRunner(TimeDuration aDelay, TimeDuration aBudget) { MOZ_ASSERT(!mDidShutdown); - TimeDuration minimumBudget = TimeDuration::FromMilliseconds(std::max( - nsRefreshDriver::HighRateMultiplier() * aBudget.ToMilliseconds(), 1.0)); + TimeDuration minimumBudget = ComputeMinimumBudgetForRunner(aBudget); if (!mCCRunner) { mCCRunner = IdleTaskRunner::Create( - CCRunnerFired, "EnsureCCRunner::CCRunnerFired", 0, aDelay, - minimumBudget, true, [this] { return mDidShutdown; }); + [this](TimeStamp aDeadline) { return CCRunnerFired(aDeadline); }, + "EnsureCCRunner::CCRunnerFired", 0, aDelay, minimumBudget, true, + [this] { return mDidShutdown; }); } else { mCCRunner->SetMinimumUsefulBudget(minimumBudget.ToMilliseconds()); nsIEventTarget* target = mozilla::GetCurrentSerialEventTarget(); diff --git a/dom/base/CCGCScheduler.h b/dom/base/CCGCScheduler.h index edb888185f65..32500e2e4f8b 100644 --- a/dom/base/CCGCScheduler.h +++ b/dom/base/CCGCScheduler.h @@ -132,7 +132,7 @@ class CCGCScheduler { mReadyForMajorGC(!mAskParentBeforeMajorGC), mInterruptRequested(false) {} - static bool CCRunnerFired(TimeStamp aDeadline); + bool CCRunnerFired(TimeStamp aDeadline); // Parameter setting @@ -178,7 +178,9 @@ class CCGCScheduler { JS::SliceBudget CreateGCSliceBudget(mozilla::TimeDuration aDuration, bool isIdle, bool isExtended) { mInterruptRequested = false; - auto budget = JS::SliceBudget(aDuration, &mInterruptRequested); + // Don't try to interrupt if we are in a mode where idle time is rare. + auto budget = JS::SliceBudget( + aDuration, mPreferFasterCollection ? nullptr : &mInterruptRequested); budget.idle = isIdle; budget.extended = isExtended; return budget; @@ -350,6 +352,8 @@ class CCGCScheduler { JS::SliceBudget ComputeInterSliceGCBudget(TimeStamp aDeadline, TimeStamp aNow); + TimeDuration ComputeMinimumBudgetForRunner(TimeDuration aBaseValue); + bool ShouldForgetSkippable(uint32_t aSuspectedCCObjects) const { // Only do a forget skippable if there are more than a few new objects // or we're doing the initial forget skippables. @@ -451,6 +455,8 @@ class CCGCScheduler { JS::SliceBudget ComputeForgetSkippableBudget(TimeStamp aStartTimeStamp, TimeStamp aDeadline); + bool PreferFasterCollection() const { return mPreferFasterCollection; } + private: // State @@ -523,6 +529,9 @@ class CCGCScheduler { bool mIsCollectingCycles = false; bool mUserIsActive = true; + bool mCurrentCollectionHasSeenNonIdle = false; + bool mPreferFasterCollection = false; + public: uint32_t mCCollectedWaitingForGC = 0; uint32_t mCCollectedZonesWaitingForGC = 0; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 9ebc95dd3fb5..11130fd145e1 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -55,6 +55,7 @@ #include "mozilla/AutoRestore.h" #include "mozilla/BasePrincipal.h" #include "mozilla/CycleCollectorStats.h" +#include "mozilla/MainThreadIdlePeriod.h" #include "mozilla/PresShell.h" #include "mozilla/SchedulerGroup.h" #include "mozilla/StaticPrefs_dom.h" @@ -1246,7 +1247,7 @@ void nsJSContext::EndCycleCollectionCallback( "A max duration ICC shouldn't reduce GC delay to 0"); TimeDuration delay; - if (aResults.mFreedGCed > 10000 && aResults.mFreedRefCounted > 10000) { + if (sScheduler->PreferFasterCollection()) { // If we collected lots of objects, trigger the next GC sooner so that // GC can cut JS-to-native edges and native objects can be then deleted. delay = TimeDuration::FromMilliseconds( @@ -1268,10 +1269,17 @@ void nsJSContext::EndCycleCollectionCallback( #endif } -/* static */ bool CCGCScheduler::CCRunnerFired(TimeStamp aDeadline) { AUTO_PROFILER_LABEL_RELEVANT_FOR_JS("Incremental CC", GCCC); + if (!aDeadline) { + mCurrentCollectionHasSeenNonIdle = true; + } else if (mPreferFasterCollection) { + // We found some idle time, try to utilize that a bit more given that + // we're in a mode where idle time is rare. + aDeadline = aDeadline + TimeDuration::FromMilliseconds(5.0); + } + bool didDoWork = false; // The CC/GC scheduler (sScheduler) decides what action(s) to take during