From 1aa9dc0f8f04dfc3d5d26bb7662cc3cd3a272b1a Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Wed, 16 Jul 2014 10:01:19 +0100 Subject: [PATCH] Bug 1017141 - Make empty chunk count constants GC params r=terrence --- js/src/builtin/TestingFunctions.cpp | 4 ++- js/src/gc/GCRuntime.h | 2 ++ js/src/jit-test/tests/gc/bug-1017141.js | 25 ++++++++++++++++ js/src/jsapi.h | 11 ++++++- js/src/jsgc.cpp | 38 ++++++++++++++----------- 5 files changed, 62 insertions(+), 18 deletions(-) create mode 100644 js/src/jit-test/tests/gc/bug-1017141.js diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 378fb5c8072c..c600c7dbcd73 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -291,7 +291,9 @@ static const struct ParamPair { {"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. diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index b43a53297290..09bba9441162 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -480,6 +480,8 @@ class GCRuntime bool dynamicHeapGrowth; bool dynamicMarkSlice; uint64_t decommitThreshold; + unsigned minEmptyChunkCount; + unsigned maxEmptyChunkCount; /* During shutdown, the GC needs to clean up every possible object. */ bool cleanUpEverything; diff --git a/js/src/jit-test/tests/gc/bug-1017141.js b/js/src/jit-test/tests/gc/bug-1017141.js new file mode 100644 index 000000000000..9c4533924ca0 --- /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(); diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 8e93b6d0f817..a663e23aa8e8 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -2145,7 +2145,16 @@ typedef enum JSGCParamKey { * 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) diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index caf89a8782e3..81d99dc5e1c2 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -238,18 +238,6 @@ 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, @@ -695,14 +683,15 @@ GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll) * 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(); @@ -716,7 +705,8 @@ GCRuntime::expireChunkPool(bool shrinkBuffers, bool releaseAll) 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; } @@ -1032,7 +1022,7 @@ GCRuntime::wantBackgroundAllocation() const * of them. */ return helperState.canBackgroundAllocate() && - chunkPool.getEmptyCount() < MIN_EMPTY_CHUNK_COUNT && + chunkPool.getEmptyCount() < minEmptyChunkCount && chunkSet.count() >= 4; } @@ -1137,6 +1127,8 @@ GCRuntime::GCRuntime(JSRuntime *rt) : dynamicHeapGrowth(false), dynamicMarkSlice(false), decommitThreshold(32 * 1024 * 1024), + minEmptyChunkCount(1), + maxEmptyChunkCount(30), cleanUpEverything(false), grayBitsValid(false), isNeeded(0), @@ -1456,6 +1448,16 @@ GCRuntime::setParameter(JSGCParamKey key, uint32_t value) 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); @@ -1504,6 +1506,10 @@ GCRuntime::getParameter(JSGCParamKey key) 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);