diff --git a/js/public/GCAPI.h b/js/public/GCAPI.h index 9928da6197d0f..4abbc8b5f08f4 100644 --- a/js/public/GCAPI.h +++ b/js/public/GCAPI.h @@ -265,6 +265,24 @@ typedef enum JSGCParamKey { * Pref: None */ JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION = 27, + + /** + * If this percentage of the nursery is tenured, then proceed to examine which + * groups we should pretenure. + * + * Default: PretenureThreshold + * Pref: None + */ + JSGC_PRETENURE_THRESHOLD = 28, + + /** + * If the above condition is met, then any object group that tenures more than + * this number of objects will be pretenured (if it can be). + * + * Default: PretenureGroupThreshold + * Pref: None + */ + JSGC_PRETENURE_GROUP_THRESHOLD = 29, } JSGCParamKey; /* diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp index da7bdfcd9c806..bf82e070c4b75 100644 --- a/js/src/gc/GC.cpp +++ b/js/src/gc/GC.cpp @@ -348,6 +348,12 @@ static const bool CompactingEnabled = true; static const uint32_t NurseryFreeThresholdForIdleCollection = Nursery::NurseryChunkUsableSize / 4; +/* JSGC_PRETENURE_THRESHOLD */ +static const float PretenureThreashold = 0.6f; + +/* JSGC_PRETENURE_GROUP_THRESHOLD */ +static const float PretenureGroupThreshold = 3000; + } // namespace TuningDefaults } // namespace gc } // namespace js @@ -1464,6 +1470,20 @@ bool GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, } nurseryFreeThresholdForIdleCollection_ = value; break; + case JSGC_PRETENURE_THRESHOLD: { + // 100 disables pretenuring + if (value == 0 || value > 100) { + return false; + } + pretenureThreshold_ = value / 100.0f; + break; + } + case JSGC_PRETENURE_GROUP_THRESHOLD: + if (value <= 0) { + return false; + } + pretenureGroupThreshold_ = value; + break; default: MOZ_CRASH("Unknown GC parameter."); } @@ -1551,7 +1571,9 @@ GCSchedulingTunables::GCSchedulingTunables() minEmptyChunkCount_(TuningDefaults::MinEmptyChunkCount), maxEmptyChunkCount_(TuningDefaults::MaxEmptyChunkCount), nurseryFreeThresholdForIdleCollection_( - TuningDefaults::NurseryFreeThresholdForIdleCollection) {} + TuningDefaults::NurseryFreeThresholdForIdleCollection), + pretenureThreshold_(TuningDefaults::PretenureThreashold), + pretenureGroupThreshold_(TuningDefaults::PretenureGroupThreshold) {} void GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) { switch (key) { @@ -1633,6 +1655,12 @@ void GCSchedulingTunables::resetParameter(JSGCParamKey key, nurseryFreeThresholdForIdleCollection_ = TuningDefaults::NurseryFreeThresholdForIdleCollection; break; + case JSGC_PRETENURE_THRESHOLD: + pretenureThreshold_ = TuningDefaults::PretenureThreashold; + break; + case JSGC_PRETENURE_GROUP_THRESHOLD: + pretenureGroupThreshold_ = TuningDefaults::PretenureGroupThreshold; + break; default: MOZ_CRASH("Unknown GC parameter."); } @@ -1691,6 +1719,10 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) { return tunables.maxEmptyChunkCount(); case JSGC_COMPACTING_ENABLED: return compactingEnabled; + case JSGC_PRETENURE_THRESHOLD: + return uint32_t(tunables.pretenureThreshold() * 100); + case JSGC_PRETENURE_GROUP_THRESHOLD: + return tunables.pretenureGroupThreshold(); default: MOZ_ASSERT(key == JSGC_NUMBER); return uint32_t(number); diff --git a/js/src/gc/GCInternals.h b/js/src/gc/GCInternals.h index ef157d6d7d6d1..a1a282725fedc 100644 --- a/js/src/gc/GCInternals.h +++ b/js/src/gc/GCInternals.h @@ -175,7 +175,7 @@ struct MovingTracer : JS::CallbackTracer { // been tenured during a minor collection. struct TenureCount { ObjectGroup* group; - int count; + unsigned count; // ObjectGroups are never nursery-allocated, and TenureCounts are only used // in minor GC (not compacting GC), so prevent the analysis from diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 70e3e614e2230..d3222d244c6e6 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -768,13 +768,15 @@ void js::Nursery::collect(JS::gcreason::Reason reason) { bool validPromotionRate; const float promotionRate = calcPromotionRate(&validPromotionRate); uint32_t pretenureCount = 0; - bool shouldPretenure = (validPromotionRate && promotionRate > 0.6) || - IsFullStoreBufferReason(reason); + bool shouldPretenure = tunables().attemptPretenuring() && + ((validPromotionRate && + promotionRate > tunables().pretenureThreshold()) || + IsFullStoreBufferReason(reason)); if (shouldPretenure) { JSContext* cx = rt->mainContextFromOwnThread(); for (auto& entry : tenureCounts.entries) { - if (entry.count >= 3000) { + if (entry.count >= tunables().pretenureGroupThreshold()) { ObjectGroup* group = entry.group; AutoRealm ar(cx, group); AutoSweepObjectGroup sweep(group); diff --git a/js/src/gc/Scheduling.h b/js/src/gc/Scheduling.h index 41ad43dd18fd4..baa2d41470a04 100644 --- a/js/src/gc/Scheduling.h +++ b/js/src/gc/Scheduling.h @@ -433,6 +433,24 @@ class GCSchedulingTunables { */ UnprotectedData nurseryFreeThresholdForIdleCollection_; + /* + * JSGC_PRETENURE_THRESHOLD + * + * Fraction of objects tenured to trigger pretenuring (between 0 and 1). If + * this fraction is met, the GC proceeds to calculate which objects will be + * tenured. If this is 1.0f (actually if it is not < 1.0f) then pretenuring + * is disabled. + */ + UnprotectedData pretenureThreshold_; + + /* + * JSGC_PRETENURE_GROUP_THRESHOLD + * + * During a single nursery collection, if this many objects from the same + * object group are tenured, then that group will be pretenured. + */ + UnprotectedData pretenureGroupThreshold_; + public: GCSchedulingTunables(); @@ -471,6 +489,10 @@ class GCSchedulingTunables { return nurseryFreeThresholdForIdleCollection_; } + bool attemptPretenuring() const { return pretenureThreshold_ < 1.0f; } + float pretenureThreshold() const { return pretenureThreshold_; } + uint32_t pretenureGroupThreshold() const { return pretenureGroupThreshold_; } + MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock); void resetParameter(JSGCParamKey key, const AutoLockGC& lock);