Bug 1507377 - Add tunables for pretenure thresholds r=jonco
authorPaul Bone <pbone@mozilla.com>
Fri, 14 Dec 2018 11:25:10 +1100
changeset 451004 f3a6aa5f0ca664fb25a7b346483d93f8b7c515d4
parent 451003 3c0bf46f5bb7027cfc719d39185decd5619f3a02
child 451005 3419d7dc0a29460dd5ea0e7f5fa160dee39fa6d4
push id110589
push userpbone@mozilla.com
push dateTue, 18 Dec 2018 00:58:40 +0000
treeherdermozilla-inbound@3419d7dc0a29 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjonco
bugs1507377
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1507377 - Add tunables for pretenure thresholds r=jonco
js/public/GCAPI.h
js/src/gc/GC.cpp
js/src/gc/GCInternals.h
js/src/gc/Nursery.cpp
js/src/gc/Scheduling.h
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -260,16 +260,34 @@ typedef enum JSGCParamKey {
   /**
    * Attempt to run a minor GC in the idle time if the free space falls
    * below this threshold.
    *
    * Default: NurseryChunkUsableSize / 4
    * 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;
 
 /*
  * Generic trace operation that calls JS::TraceEdge on each traceable thing's
  * location reachable from data.
  */
 typedef void (*JSTraceDataOp)(JSTracer* trc, void* data);
 
--- a/js/src/gc/GC.cpp
+++ b/js/src/gc/GC.cpp
@@ -343,16 +343,22 @@ static const JSGCMode Mode = JSGC_MODE_I
 
 /* JSGC_COMPACTING_ENABLED */
 static const bool CompactingEnabled = true;
 
 /* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION */
 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
 
 /*
  * We start to incremental collection for a zone when a proportion of its
  * threshold is reached. This is configured by the
  * JSGC_ALLOCATION_THRESHOLD_FACTOR and
@@ -1459,16 +1465,30 @@ bool GCSchedulingTunables::setParameter(
       setMaxEmptyChunkCount(value);
       break;
     case JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION:
       if (value > gcMaxNurseryBytes()) {
         value = gcMaxNurseryBytes();
       }
       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.");
   }
 
   return true;
 }
 
 void GCSchedulingTunables::setMaxMallocBytes(size_t value) {
@@ -1546,17 +1566,19 @@ GCSchedulingTunables::GCSchedulingTunabl
       highFrequencyHighLimitBytes_(TuningDefaults::HighFrequencyHighLimitBytes),
       highFrequencyHeapGrowthMax_(TuningDefaults::HighFrequencyHeapGrowthMax),
       highFrequencyHeapGrowthMin_(TuningDefaults::HighFrequencyHeapGrowthMin),
       lowFrequencyHeapGrowth_(TuningDefaults::LowFrequencyHeapGrowth),
       dynamicMarkSliceEnabled_(TuningDefaults::DynamicMarkSliceEnabled),
       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) {
     case JSGC_MAX_MALLOC_BYTES:
       setMaxMallocBytes(TuningDefaults::MaxMallocBytes, lock);
       break;
     case JSGC_SLICE_TIME_BUDGET:
       defaultTimeBudget_ = TuningDefaults::DefaultTimeBudget;
@@ -1628,16 +1650,22 @@ void GCSchedulingTunables::resetParamete
       break;
     case JSGC_MAX_EMPTY_CHUNK_COUNT:
       setMaxEmptyChunkCount(TuningDefaults::MaxEmptyChunkCount);
       break;
     case JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION:
       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.");
   }
 }
 
 uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
   switch (key) {
     case JSGC_MAX_BYTES:
@@ -1686,16 +1714,20 @@ uint32_t GCRuntime::getParameter(JSGCPar
     case JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT:
       return uint32_t(tunables.allocThresholdFactorAvoidInterrupt() * 100);
     case JSGC_MIN_EMPTY_CHUNK_COUNT:
       return tunables.minEmptyChunkCount(lock);
     case JSGC_MAX_EMPTY_CHUNK_COUNT:
       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);
   }
 }
 
 void GCRuntime::setMarkStackLimit(size_t limit, AutoLockGC& lock) {
   MOZ_ASSERT(!JS::RuntimeHeapIsBusy());
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -170,17 +170,17 @@ struct MovingTracer : JS::CallbackTracer
   template <typename T>
   void updateEdge(T** thingp);
 };
 
 // Structure for counting how many times objects in a particular group have
 // 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
   // complaining about TenureCounts being held live across a minor GC.
 } JS_HAZ_NON_GC_POINTER;
 
 // Keep rough track of how many times we tenure objects in particular groups
 // during minor collections, using a fixed size hash for efficiency at the cost
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -763,23 +763,25 @@ void js::Nursery::collect(JS::gcreason::
   // If we are promoting the nursery, or exhausted the store buffer with
   // pointers to nursery things, which will force a collection well before
   // the nursery is full, look for object groups that are getting promoted
   // excessively and try to pretenure them.
   startProfile(ProfileKey::Pretenure);
   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);
         if (group->canPreTenure(sweep)) {
           group->setShouldPreTenure(sweep, cx);
           pretenureCount++;
         }
       }
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -428,16 +428,34 @@ class GCSchedulingTunables {
   /*
    * JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION
    *
    * Attempt to run a minor GC in the idle time if the free space falls
    * below this threshold.
    */
   UnprotectedData<uint32_t> 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<float> 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<uint32_t> pretenureGroupThreshold_;
+
  public:
   GCSchedulingTunables();
 
   size_t gcMaxBytes() const { return gcMaxBytes_; }
   size_t maxMallocBytes() const { return maxMallocBytes_; }
   size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; }
   size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
   double allocThresholdFactor() const { return allocThresholdFactor_; }
@@ -466,16 +484,20 @@ class GCSchedulingTunables {
   unsigned minEmptyChunkCount(const AutoLockGC&) const {
     return minEmptyChunkCount_;
   }
   unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
   uint32_t nurseryFreeThresholdForIdleCollection() const {
     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);
 
   void setMaxMallocBytes(size_t value);
 
  private:
   void setHighFrequencyLowLimit(size_t value);