Bug 1506761 - Add a fraction threshold for collecting the nursery r=sfink
authorPaul Bone <pbone@mozilla.com>
Wed, 27 Feb 2019 03:36:54 +0000
changeset 519284 df61ae31e2a8f47b6f07f4bf0e3f5fc8b252bc4c
parent 519283 9cc065c29f512dfc300ebcce54c3511bc12a139b
child 519285 1a6bfb6eba8a3c91cee898badfff9a73a72ad8ab
push id10862
push userffxbld-merge
push dateMon, 11 Mar 2019 13:01:11 +0000
treeherdermozilla-beta@a2e7f5c935da [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfink
bugs1506761
milestone67.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 1506761 - Add a fraction threshold for collecting the nursery r=sfink We collect the nursery in idle time if there is less than 256KB of space remaining. However when the nursery is small this doesn't make sense, so add a percentage-based threshold to be used when the nursery is small. Differential Revision: https://phabricator.services.mozilla.com/D20247
js/public/GCAPI.h
js/src/gc/GC.cpp
js/src/gc/Nursery.cpp
js/src/gc/Scheduling.h
--- a/js/public/GCAPI.h
+++ b/js/public/GCAPI.h
@@ -278,16 +278,26 @@ typedef enum JSGCParamKey {
   /**
    * 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,
+
+  /**
+   * Attempt to run a minor GC in the idle time if the free space falls
+   * below this percentage (from 0 to 99).
+   *
+   * Default: 25
+   * Pref: None
+   */
+  JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT = 30,
+
 } 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
@@ -342,16 +342,19 @@ 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_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT */
+static const float NurseryFreeThresholdForIdleCollectionFraction = 0.25f;
+
 /* JSGC_PRETENURE_THRESHOLD */
 static const float PretenureThreashold = 0.6f;
 
 /* JSGC_PRETENURE_GROUP_THRESHOLD */
 static const float PretenureGroupThreshold = 3000;
 
 }  // namespace TuningDefaults
 }  // namespace gc
@@ -1471,16 +1474,22 @@ bool GCSchedulingTunables::setParameter(
       setMaxEmptyChunkCount(value);
       break;
     case JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION:
       if (value > gcMaxNurseryBytes()) {
         value = gcMaxNurseryBytes();
       }
       nurseryFreeThresholdForIdleCollection_ = value;
       break;
+    case JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT:
+      if (value == 0 || value > 100) {
+        return false;
+      }
+      nurseryFreeThresholdForIdleCollectionFraction_ = value / 100.0f;
+      break;
     case JSGC_PRETENURE_THRESHOLD: {
       // 100 disables pretenuring
       if (value == 0 || value > 100) {
         return false;
       }
       pretenureThreshold_ = value / 100.0f;
       break;
     }
@@ -1573,16 +1582,18 @@ GCSchedulingTunables::GCSchedulingTunabl
       highFrequencyHeapGrowthMax_(TuningDefaults::HighFrequencyHeapGrowthMax),
       highFrequencyHeapGrowthMin_(TuningDefaults::HighFrequencyHeapGrowthMin),
       lowFrequencyHeapGrowth_(TuningDefaults::LowFrequencyHeapGrowth),
       dynamicMarkSliceEnabled_(TuningDefaults::DynamicMarkSliceEnabled),
       minEmptyChunkCount_(TuningDefaults::MinEmptyChunkCount),
       maxEmptyChunkCount_(TuningDefaults::MaxEmptyChunkCount),
       nurseryFreeThresholdForIdleCollection_(
           TuningDefaults::NurseryFreeThresholdForIdleCollection),
+      nurseryFreeThresholdForIdleCollectionFraction_(
+          TuningDefaults::NurseryFreeThresholdForIdleCollectionFraction),
       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;
@@ -1656,16 +1667,20 @@ 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_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT:
+      nurseryFreeThresholdForIdleCollectionFraction_ =
+          TuningDefaults::NurseryFreeThresholdForIdleCollectionFraction;
+      break;
     case JSGC_PRETENURE_THRESHOLD:
       pretenureThreshold_ = TuningDefaults::PretenureThreashold;
       break;
     case JSGC_PRETENURE_GROUP_THRESHOLD:
       pretenureGroupThreshold_ = TuningDefaults::PretenureGroupThreshold;
       break;
     default:
       MOZ_CRASH("Unknown GC parameter.");
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -693,18 +693,45 @@ inline void js::Nursery::startProfile(Pr
 }
 
 inline void js::Nursery::endProfile(ProfileKey key) {
   profileDurations_[key] = ReallyNow() - startTimes_[key];
   totalDurations_[key] += profileDurations_[key];
 }
 
 bool js::Nursery::shouldCollect() const {
-  uint32_t threshold = tunables().nurseryFreeThresholdForIdleCollection();
-  return !isEmpty() && (minorGCRequested() || freeSpace() < threshold);
+  if (minorGCRequested()) {
+    return true;
+  }
+
+  bool belowBytesThreshold =
+      freeSpace() < tunables().nurseryFreeThresholdForIdleCollection();
+  bool belowFractionThreshold =
+      float(freeSpace()) / float(capacity()) <
+      tunables().nurseryFreeThresholdForIdleCollectionFraction();
+
+  // We want to use belowBytesThreshold when the nursery is sufficiently large,
+  // and belowFractionThreshold when it's small.
+  //
+  // When the nursery is small then belowBytesThreshold is a lower threshold
+  // (triggered earlier) than belowFractionThreshold.  So if the fraction
+  // threshold is true, the bytes one will be true also.  The opposite is true
+  // when the nursery is large.
+  //
+  // Therefore, by the time we cross the threshold we care about, we've already
+  // crossed the other one, and we can boolean AND to use either condition
+  // without encoding any "is the nursery big/small" test/threshold.  The point
+  // at which they cross is when the nursery is:  BytesThreshold /
+  // FractionThreshold large.
+  //
+  // With defaults that's:
+  //
+  //   1MB = 256KB / 0.25
+  //
+  return belowBytesThreshold && belowFractionThreshold;
 }
 
 static inline bool IsFullStoreBufferReason(JS::GCReason reason) {
   return reason == JS::GCReason::FULL_WHOLE_CELL_BUFFER ||
          reason == JS::GCReason::FULL_GENERIC_BUFFER ||
          reason == JS::GCReason::FULL_VALUE_BUFFER ||
          reason == JS::GCReason::FULL_CELL_PTR_BUFFER ||
          reason == JS::GCReason::FULL_SLOT_BUFFER ||
--- a/js/src/gc/Scheduling.h
+++ b/js/src/gc/Scheduling.h
@@ -422,21 +422,24 @@ class GCSchedulingTunables {
    *
    * Controls the number of empty chunks reserved for future allocation.
    */
   UnprotectedData<uint32_t> minEmptyChunkCount_;
   UnprotectedData<uint32_t> maxEmptyChunkCount_;
 
   /*
    * JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION
+   * JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_FRACTION
    *
    * Attempt to run a minor GC in the idle time if the free space falls
-   * below this threshold.
+   * below this threshold. The absolute threshold is used when the nursery is
+   * large and the percentage when it is small.  See Nursery::shouldCollect()
    */
   UnprotectedData<uint32_t> nurseryFreeThresholdForIdleCollection_;
+  UnprotectedData<float> nurseryFreeThresholdForIdleCollectionFraction_;
 
   /*
    * 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.
@@ -483,16 +486,19 @@ class GCSchedulingTunables {
   bool isDynamicMarkSliceEnabled() const { return dynamicMarkSliceEnabled_; }
   unsigned minEmptyChunkCount(const AutoLockGC&) const {
     return minEmptyChunkCount_;
   }
   unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; }
   uint32_t nurseryFreeThresholdForIdleCollection() const {
     return nurseryFreeThresholdForIdleCollection_;
   }
+  float nurseryFreeThresholdForIdleCollectionFraction() const {
+    return nurseryFreeThresholdForIdleCollectionFraction_;
+  }
 
   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);