Bug 1929640 - Add a mode to CCGCScheduler where it can collect faster in case lots of garbage is created and there isn't too much idle time to run the slices, r=mccr8,sfink

Differential Revision: https://phabricator.services.mozilla.com/D228215
This commit is contained in:
Olli Pettay 2024-11-10 17:58:12 +00:00
parent c931229d0e
commit 07a2abbaab
3 changed files with 50 additions and 12 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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