From a559ffee38b6efe949058c00371d40e0694474fb Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 26 Apr 2021 22:38:31 +0000 Subject: [PATCH] Bug 1703374 - Provide SliceBudget users with more control of when and how often time checks happen r=jonco Differential Revision: https://phabricator.services.mozilla.com/D111008 --- dom/base/CCGCScheduler.h | 8 ++++++-- js/public/SliceBudget.h | 17 ++++++++++++++--- js/src/gc/GC.cpp | 6 +++--- xpcom/base/nsCycleCollector.cpp | 9 ++------- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/dom/base/CCGCScheduler.h b/dom/base/CCGCScheduler.h index cd2c46eeff00..2af246f45118 100644 --- a/dom/base/CCGCScheduler.h +++ b/dom/base/CCGCScheduler.h @@ -58,6 +58,9 @@ static const TimeDuration kMaxCCLockedoutTime = TimeDuration::FromSeconds(30); // Trigger a CC if the purple buffer exceeds this size when we check it. static const uint32_t kCCPurpleLimit = 200; +// How many cycle collected nodes to traverse between time checks. +static const int64_t kNumCCNodesBetweenTimeChecks = 1000; + enum class CCRunnerAction { None, ForgetSkippable, @@ -351,7 +354,7 @@ js::SliceBudget CCGCScheduler::ComputeCCSliceBudget( if (aCCBeginTime.IsNull()) { // If no CC is in progress, use the standard slice time. - return js::SliceBudget(baseBudget); + return js::SliceBudget(js::TimeBudget(baseBudget), kNumCCNodesBetweenTimeChecks); } // Only run a limited slice if we're within the max running time. @@ -380,7 +383,8 @@ js::SliceBudget CCGCScheduler::ComputeCCSliceBudget( // baseBudget will be negative and we will end up returning // laterSliceBudget. return js::SliceBudget( - std::max({delaySliceBudget, laterSliceBudget, baseBudget})); + js::TimeBudget(std::max({delaySliceBudget, laterSliceBudget, baseBudget})), + kNumCCNodesBetweenTimeChecks); } inline TimeDuration CCGCScheduler::ComputeInterSliceGCBudget( diff --git a/js/public/SliceBudget.h b/js/public/SliceBudget.h index 6282ff509d51..d95fd29a97f7 100644 --- a/js/public/SliceBudget.h +++ b/js/public/SliceBudget.h @@ -22,6 +22,7 @@ struct JS_PUBLIC_API TimeBudget { mozilla::TimeStamp deadline; // Calculated when SliceBudget is constructed. explicit TimeBudget(int64_t milliseconds) : budget(milliseconds) {} + explicit TimeBudget(mozilla::TimeDuration duration) : TimeBudget(duration.ToMilliseconds()) {} }; struct JS_PUBLIC_API WorkBudget { @@ -43,8 +44,11 @@ struct UnlimitedBudget {}; */ class JS_PUBLIC_API SliceBudget { static const intptr_t UnlimitedCounter = INTPTR_MAX; + static const intptr_t DefaultStepsPerTimeCheck = 1000; mozilla::Variant budget; + int64_t stepsPerTimeCheck = DefaultStepsPerTimeCheck; + int64_t counter; SliceBudget() : budget(UnlimitedBudget()), counter(UnlimitedCounter) {} @@ -52,13 +56,11 @@ class JS_PUBLIC_API SliceBudget { bool checkOverBudget(); public: - static const intptr_t StepsPerTimeCheck = 1000; - // Use to create an unlimited budget. static SliceBudget unlimited() { return SliceBudget(); } // Instantiate as SliceBudget(TimeBudget(n)). - explicit SliceBudget(TimeBudget time); + explicit SliceBudget(TimeBudget time, int64_t stepsPerTimeCheck = DefaultStepsPerTimeCheck); // Instantiate as SliceBudget(WorkBudget(n)). explicit SliceBudget(WorkBudget work); @@ -66,11 +68,20 @@ class JS_PUBLIC_API SliceBudget { explicit SliceBudget(mozilla::TimeDuration time) : SliceBudget(TimeBudget(time.ToMilliseconds())) {} + // Register having performed the given number of steps (counted against a + // work budget, or progress towards the next time or callback check). void step(uint64_t steps = 1) { MOZ_ASSERT(steps > 0); counter -= steps; } + // Do enough steps to force an "expensive" (time and/or callback) check on + // the next call to isOverBudget. Useful when switching between major phases + // of an operation like a cycle collection. + void stepAndForceCheck() { + counter = 0; + } + bool isOverBudget() { return counter <= 0 && checkOverBudget(); } bool isWorkBudget() const { return budget.is(); } diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index 812c3a71e712..7342d89a1f9f 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -3175,8 +3175,8 @@ void ArenaLists::checkNoArenasToUpdateForKind(AllocKind kind) { #endif } -SliceBudget::SliceBudget(TimeBudget time) - : budget(time), counter(StepsPerTimeCheck) { +SliceBudget::SliceBudget(TimeBudget time, int64_t stepsPerTimeCheckArg) + : budget(TimeBudget(time)), stepsPerTimeCheck(stepsPerTimeCheckArg), counter(stepsPerTimeCheckArg) { budget.as().deadline = ReallyNow() + TimeDuration::FromMilliseconds(timeBudget()); } @@ -3206,7 +3206,7 @@ bool SliceBudget::checkOverBudget() { return true; } - counter = StepsPerTimeCheck; + counter = stepsPerTimeCheck; return false; } diff --git a/xpcom/base/nsCycleCollector.cpp b/xpcom/base/nsCycleCollector.cpp index 34f71888bcc5..47704eeaea7e 100644 --- a/xpcom/base/nsCycleCollector.cpp +++ b/xpcom/base/nsCycleCollector.cpp @@ -2038,10 +2038,6 @@ void CCGraphBuilder::DoneAddingRoots() { } MOZ_NEVER_INLINE bool CCGraphBuilder::BuildGraph(SliceBudget& aBudget) { - const intptr_t kNumNodesBetweenTimeChecks = 1000; - const intptr_t kStep = - SliceBudget::StepsPerTimeCheck / kNumNodesBetweenTimeChecks; - MOZ_ASSERT(mCurrNode); while (!aBudget.isOverBudget() && !mCurrNode->IsDone()) { @@ -2068,7 +2064,7 @@ MOZ_NEVER_INLINE bool CCGraphBuilder::BuildGraph(SliceBudget& aBudget) { SetLastChild(); } - aBudget.step(kStep * (mNoteChildCount + 1)); + aBudget.step(mNoteChildCount + 1); } if (!mCurrNode->IsDone()) { @@ -3446,8 +3442,7 @@ bool nsCycleCollector::Collect(ccType aCCType, SliceBudget& aBudget, break; } if (continueSlice) { - // Force SliceBudget::isOverBudget to check the time. - aBudget.step(SliceBudget::StepsPerTimeCheck); + aBudget.stepAndForceCheck(); continueSlice = !aBudget.isOverBudget(); } } while (continueSlice);