Bug 1017141 - Make empty chunk count constants GC params r=terrence
authorJon Coppeard <jcoppeard@mozilla.com>
Wed, 16 Jul 2014 10:01:19 +0100
changeset 216273 1b29d99921e9d6a8726e5c4df60ef18181168eea
parent 216272 6f0a5bfa00a66e8be137eb39dbdc65210739e714
child 216274 40e75ff09974f1869d2b750994557912b63eeffa
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1017141
milestone33.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 1017141 - Make empty chunk count constants GC params r=terrence
js/src/builtin/TestingFunctions.cpp
js/src/gc/GCRuntime.h
js/src/jit-test/tests/gc/bug-1017141.js
js/src/jsapi.h
js/src/jsgc.cpp
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -286,17 +286,19 @@ static const struct ParamPair {
     const char      *name;
     JSGCParamKey    param;
 } paramMap[] = {
     {"maxBytes",            JSGC_MAX_BYTES },
     {"maxMallocBytes",      JSGC_MAX_MALLOC_BYTES},
     {"gcBytes",             JSGC_BYTES},
     {"gcNumber",            JSGC_NUMBER},
     {"sliceTimeBudget",     JSGC_SLICE_TIME_BUDGET},
-    {"markStackLimit",      JSGC_MARK_STACK_LIMIT}
+    {"markStackLimit",      JSGC_MARK_STACK_LIMIT},
+    {"minEmptyChunkCount",  JSGC_MIN_EMPTY_CHUNK_COUNT},
+    {"maxEmptyChunkCount",  JSGC_MAX_EMPTY_CHUNK_COUNT}
 };
 
 // Keep this in sync with above params.
 #define GC_PARAMETER_ARGS_LIST "maxBytes, maxMallocBytes, gcBytes, gcNumber, sliceTimeBudget, or markStackLimit"
 
 static bool
 GCParameter(JSContext *cx, unsigned argc, Value *vp)
 {
--- a/js/src/gc/GCRuntime.h
+++ b/js/src/gc/GCRuntime.h
@@ -475,16 +475,18 @@ class GCRuntime
     uint64_t              highFrequencyLowLimitBytes;
     uint64_t              highFrequencyHighLimitBytes;
     double                highFrequencyHeapGrowthMax;
     double                highFrequencyHeapGrowthMin;
     double                lowFrequencyHeapGrowth;
     bool                  dynamicHeapGrowth;
     bool                  dynamicMarkSlice;
     uint64_t              decommitThreshold;
+    unsigned              minEmptyChunkCount;
+    unsigned              maxEmptyChunkCount;
 
     /* During shutdown, the GC needs to clean up every possible object. */
     bool                  cleanUpEverything;
 
     /*
      * The gray bits can become invalid if UnmarkGray overflows the stack. A
      * full GC will reset this bit, since it fills in all the gray bits.
      */
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1017141.js
@@ -0,0 +1,25 @@
+var min = gcparam('minEmptyChunkCount');
+var max = gcparam('maxEmptyChunkCount');
+
+gcparam('minEmptyChunkCount', 10);
+gcparam('maxEmptyChunkCount', 20);
+assertEq(gcparam('minEmptyChunkCount'), 10);
+assertEq(gcparam('maxEmptyChunkCount'), 20);
+gc();
+
+/* We maintain the invariant that maxEmptyChunkCount >= minEmptyChunkCount. */
+gcparam('minEmptyChunkCount', 30);
+assertEq(gcparam('minEmptyChunkCount'), 30);
+assertEq(gcparam('maxEmptyChunkCount'), 30);
+gc();
+
+gcparam('maxEmptyChunkCount', 5);
+assertEq(gcparam('minEmptyChunkCount'), 5);
+assertEq(gcparam('maxEmptyChunkCount'), 5);
+gc();
+
+gcparam('minEmptyChunkCount', min);
+gcparam('maxEmptyChunkCount', max);
+assertEq(gcparam('minEmptyChunkCount'), min);
+assertEq(gcparam('maxEmptyChunkCount'), max);
+gc();
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2140,17 +2140,26 @@ typedef enum JSGCParamKey {
     /* Lower limit after which we limit the heap growth. */
     JSGC_ALLOCATION_THRESHOLD = 19,
 
     /*
      * We decommit memory lazily. If more than this number of megabytes is
      * available to be decommitted, then JS_MaybeGC will trigger a shrinking GC
      * to decommit it.
      */
-    JSGC_DECOMMIT_THRESHOLD = 20
+    JSGC_DECOMMIT_THRESHOLD = 20,
+
+    /*
+     * We try to keep at least this many unused chunks in the free chunk pool at
+     * all times, even after a shrinking GC.
+     */
+    JSGC_MIN_EMPTY_CHUNK_COUNT = 21,
+
+    /* We never keep more than this many unused chunks in the free chunk pool. */
+    JSGC_MAX_EMPTY_CHUNK_COUNT = 22
 } JSGCParamKey;
 
 extern JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32_t value);
 
 extern JS_PUBLIC_API(uint32_t)
 JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -233,28 +233,16 @@ using mozilla::Swap;
 using JS::AutoGCRooter;
 
 /* Perform a Full GC every 20 seconds if MaybeGC is called */
 static const uint64_t GC_IDLE_FULL_SPAN = 20 * 1000 * 1000;
 
 /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */
 static const int IGC_MARK_SLICE_MULTIPLIER = 2;
 
-#ifdef JSGC_GENERATIONAL
-static const unsigned MIN_EMPTY_CHUNK_COUNT = 1;
-#else
-static const unsigned MIN_EMPTY_CHUNK_COUNT = 0;
-#endif
-
-#if defined(ANDROID) || defined(MOZ_B2G)
-static const unsigned MAX_EMPTY_CHUNK_COUNT = 2;
-#else
-static const unsigned MAX_EMPTY_CHUNK_COUNT = 30;
-#endif
-
 const AllocKind gc::slotsToThingKind[] = {
     /* 0 */  FINALIZE_OBJECT0,  FINALIZE_OBJECT2,  FINALIZE_OBJECT2,  FINALIZE_OBJECT4,
     /* 4 */  FINALIZE_OBJECT4,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,  FINALIZE_OBJECT8,
     /* 8 */  FINALIZE_OBJECT8,  FINALIZE_OBJECT12, FINALIZE_OBJECT12, FINALIZE_OBJECT12,
     /* 12 */ FINALIZE_OBJECT12, FINALIZE_OBJECT16, FINALIZE_OBJECT16, FINALIZE_OBJECT16,
     /* 16 */ FINALIZE_OBJECT16
 };
 
@@ -690,38 +678,40 @@ Chunk *
 GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll)
 {
     /*
      * Return old empty chunks to the system while preserving the order of
      * other chunks in the list. This way, if the GC runs several times
      * without emptying the list, the older chunks will stay at the tail
      * and are more likely to reach the max age.
      */
+    JS_ASSERT(maxEmptyChunkCount >= minEmptyChunkCount);
     Chunk *freeList = nullptr;
     unsigned freeChunkCount = 0;
     for (ChunkPool::Enum e(chunkPool); !e.empty(); ) {
         Chunk *chunk = e.front();
         JS_ASSERT(chunk->unused());
         JS_ASSERT(!chunkSet.has(chunk));
-        if (releaseAll || freeChunkCount >= MAX_EMPTY_CHUNK_COUNT ||
-            (freeChunkCount >= MIN_EMPTY_CHUNK_COUNT &&
+        if (releaseAll || freeChunkCount >= maxEmptyChunkCount ||
+            (freeChunkCount >= minEmptyChunkCount &&
              (shrinkBuffers || chunk->info.age == MAX_EMPTY_CHUNK_AGE)))
         {
             e.removeAndPopFront();
             prepareToFreeChunk(chunk->info);
             chunk->info.next = freeList;
             freeList = chunk;
         } else {
             /* Keep the chunk but increase its age. */
             ++freeChunkCount;
             ++chunk->info.age;
             e.popFront();
         }
     }
-    JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= MIN_EMPTY_CHUNK_COUNT);
+    JS_ASSERT(chunkPool.getEmptyCount() <= maxEmptyChunkCount);
+    JS_ASSERT_IF(shrinkBuffers, chunkPool.getEmptyCount() <= minEmptyChunkCount);
     JS_ASSERT_IF(releaseAll, chunkPool.getEmptyCount() == 0);
     return freeList;
 }
 
 void
 GCRuntime::freeChunkList(Chunk *chunkListHead)
 {
     while (Chunk *chunk = chunkListHead) {
@@ -1027,17 +1017,17 @@ inline bool
 GCRuntime::wantBackgroundAllocation() const
 {
     /*
      * To minimize memory waste we do not want to run the background chunk
      * allocation if we have empty chunks or when the runtime needs just few
      * of them.
      */
     return helperState.canBackgroundAllocate() &&
-           chunkPool.getEmptyCount() < MIN_EMPTY_CHUNK_COUNT &&
+           chunkPool.getEmptyCount() < minEmptyChunkCount &&
            chunkSet.count() >= 4;
 }
 
 class js::gc::AutoMaybeStartBackgroundAllocation
 {
   private:
     JSRuntime *runtime;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@@ -1132,16 +1122,18 @@ GCRuntime::GCRuntime(JSRuntime *rt) :
     highFrequencyLowLimitBytes(100 * 1024 * 1024),
     highFrequencyHighLimitBytes(500 * 1024 * 1024),
     highFrequencyHeapGrowthMax(3.0),
     highFrequencyHeapGrowthMin(1.5),
     lowFrequencyHeapGrowth(1.5),
     dynamicHeapGrowth(false),
     dynamicMarkSlice(false),
     decommitThreshold(32 * 1024 * 1024),
+    minEmptyChunkCount(1),
+    maxEmptyChunkCount(30),
     cleanUpEverything(false),
     grayBitsValid(false),
     isNeeded(0),
     number(0),
     startNumber(0),
     isFull(false),
     triggerReason(JS::gcreason::NO_REASON),
 #ifdef DEBUG
@@ -1451,16 +1443,26 @@ GCRuntime::setParameter(JSGCParamKey key
         dynamicMarkSlice = value;
         break;
       case JSGC_ALLOCATION_THRESHOLD:
         allocThreshold = value * 1024 * 1024;
         break;
       case JSGC_DECOMMIT_THRESHOLD:
         decommitThreshold = value * 1024 * 1024;
         break;
+      case JSGC_MIN_EMPTY_CHUNK_COUNT:
+        minEmptyChunkCount = value;
+        if (minEmptyChunkCount > maxEmptyChunkCount)
+            maxEmptyChunkCount = minEmptyChunkCount;
+        break;
+      case JSGC_MAX_EMPTY_CHUNK_COUNT:
+        maxEmptyChunkCount = value;
+        if (minEmptyChunkCount > maxEmptyChunkCount)
+            minEmptyChunkCount = maxEmptyChunkCount;
+        break;
       default:
         JS_ASSERT(key == JSGC_MODE);
         mode = JSGCMode(value);
         JS_ASSERT(mode == JSGC_MODE_GLOBAL ||
                   mode == JSGC_MODE_COMPARTMENT ||
                   mode == JSGC_MODE_INCREMENTAL);
         return;
     }
@@ -1499,16 +1501,20 @@ GCRuntime::getParameter(JSGCParamKey key
       case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
         return uint32_t(lowFrequencyHeapGrowth * 100);
       case JSGC_DYNAMIC_HEAP_GROWTH:
         return dynamicHeapGrowth;
       case JSGC_DYNAMIC_MARK_SLICE:
         return dynamicMarkSlice;
       case JSGC_ALLOCATION_THRESHOLD:
         return allocThreshold / 1024 / 1024;
+      case JSGC_MIN_EMPTY_CHUNK_COUNT:
+        return minEmptyChunkCount;
+      case JSGC_MAX_EMPTY_CHUNK_COUNT:
+        return maxEmptyChunkCount;
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return uint32_t(number);
     }
 }
 
 void
 GCRuntime::setMarkStackLimit(size_t limit)