From 73cca4c47d0c2df0e1e84df499d81fbf5d4d4f42 Mon Sep 17 00:00:00 2001 From: Paul Bone Date: Thu, 17 Aug 2017 11:18:10 +1000 Subject: [PATCH] Bug 1386660 - Part 3: Provide code to reset SM/GC parameters. r=jonco --- js/src/gc/GCRuntime.h | 28 +++---- js/src/jsapi.cpp | 8 ++ js/src/jsapi.h | 3 + js/src/jsgc.cpp | 178 ++++++++++++++++++++++++++++++++++++++---- 4 files changed, 182 insertions(+), 35 deletions(-) diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 270bcf4cc376..21388c43486f 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -216,25 +216,7 @@ class GCSchedulingTunables UnprotectedData maxEmptyChunkCount_; public: - GCSchedulingTunables() - : gcMaxBytes_(0), - gcMaxNurseryBytes_(0), - gcZoneAllocThresholdBase_(30 * 1024 * 1024), - zoneAllocThresholdFactor_(0.9f), - zoneAllocThresholdFactorAvoidInterrupt_(0.9f), - zoneAllocDelayBytes_(1024 * 1024), - dynamicHeapGrowthEnabled_(false), - highFrequencyThresholdUsec_(1000 * 1000), - highFrequencyLowLimitBytes_(100 * 1024 * 1024), - highFrequencyHighLimitBytes_(500 * 1024 * 1024), - highFrequencyHeapGrowthMax_(3.0), - highFrequencyHeapGrowthMin_(1.5), - lowFrequencyHeapGrowth_(1.5), - dynamicMarkSliceEnabled_(false), - refreshFrameSlicesEnabled_(true), - minEmptyChunkCount_(1), - maxEmptyChunkCount_(30) - {} + GCSchedulingTunables(); size_t gcMaxBytes() const { return gcMaxBytes_; } size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; } @@ -255,6 +237,13 @@ class GCSchedulingTunables unsigned maxEmptyChunkCount() const { return maxEmptyChunkCount_; } MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, const AutoLockGC& lock); + void resetParameter(JSGCParamKey key, const AutoLockGC& lock); + +private: + void setHighFrequencyLowLimit(uint64_t value); + void setHighFrequencyHighLimit(uint64_t value); + void setMinEmptyChunkCount(uint32_t value); + void setMaxEmptyChunkCount(uint32_t value); }; /* @@ -700,6 +689,7 @@ class GCRuntime void setMarkStackLimit(size_t limit, AutoLockGC& lock); MOZ_MUST_USE bool setParameter(JSGCParamKey key, uint32_t value, AutoLockGC& lock); + void resetParameter(JSGCParamKey key, AutoLockGC& lock); uint32_t getParameter(JSGCParamKey key, const AutoLockGC& lock); MOZ_MUST_USE bool triggerGC(JS::gcreason::Reason reason); diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 309693317705..71b8b4778c4b 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -1477,6 +1477,14 @@ JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value) MOZ_ALWAYS_TRUE(cx->runtime()->gc.setParameter(key, value, lock)); } +JS_PUBLIC_API(void) +JS_ResetGCParameter(JSContext* cx, JSGCParamKey key) +{ + cx->runtime()->gc.waitBackgroundSweepEnd(); + AutoLockGC lock(cx->runtime()); + cx->runtime()->gc.resetParameter(key, lock); +} + JS_PUBLIC_API(uint32_t) JS_GetGCParameter(JSContext* cx, JSGCParamKey key) { diff --git a/js/src/jsapi.h b/js/src/jsapi.h index b952ea6e48dc..eb528e4bfc87 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -1875,6 +1875,9 @@ typedef enum JSGCParamKey { extern JS_PUBLIC_API(void) JS_SetGCParameter(JSContext* cx, JSGCParamKey key, uint32_t value); +extern JS_PUBLIC_API(void) +JS_ResetGCParameter(JSContext* cx, JSGCParamKey key); + extern JS_PUBLIC_API(uint32_t) JS_GetGCParameter(JSContext* cx, JSGCParamKey key); diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 0d554dd4456d..9c064d160b45 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1234,20 +1234,14 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL uint64_t newLimit = (uint64_t)value * 1024 * 1024; if (newLimit == UINT64_MAX) return false; - highFrequencyLowLimitBytes_ = newLimit; - if (highFrequencyLowLimitBytes_ >= highFrequencyHighLimitBytes_) - highFrequencyHighLimitBytes_ = highFrequencyLowLimitBytes_ + 1; - MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_); + setHighFrequencyLowLimit(newLimit); break; } case JSGC_HIGH_FREQUENCY_HIGH_LIMIT: { uint64_t newLimit = (uint64_t)value * 1024 * 1024; if (newLimit == 0) return false; - highFrequencyHighLimitBytes_ = newLimit; - if (highFrequencyHighLimitBytes_ <= highFrequencyLowLimitBytes_) - highFrequencyLowLimitBytes_ = highFrequencyHighLimitBytes_ - 1; - MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_); + setHighFrequencyHighLimit(newLimit); break; } case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX: { @@ -1284,16 +1278,10 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL gcZoneAllocThresholdBase_ = value * 1024 * 1024; break; case JSGC_MIN_EMPTY_CHUNK_COUNT: - minEmptyChunkCount_ = value; - if (minEmptyChunkCount_ > maxEmptyChunkCount_) - maxEmptyChunkCount_ = minEmptyChunkCount_; - MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_); + setMinEmptyChunkCount(value); break; case JSGC_MAX_EMPTY_CHUNK_COUNT: - maxEmptyChunkCount_ = value; - if (minEmptyChunkCount_ > maxEmptyChunkCount_) - minEmptyChunkCount_ = maxEmptyChunkCount_; - MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_); + setMaxEmptyChunkCount(value); break; case JSGC_REFRESH_FRAME_SLICES_ENABLED: refreshFrameSlicesEnabled_ = value != 0; @@ -1305,6 +1293,164 @@ GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value, const AutoL return true; } +void +GCSchedulingTunables::setHighFrequencyLowLimit(uint64_t newLimit) +{ + highFrequencyLowLimitBytes_ = newLimit; + if (highFrequencyLowLimitBytes_ >= highFrequencyHighLimitBytes_) + highFrequencyHighLimitBytes_ = highFrequencyLowLimitBytes_ + 1; + MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_); +} + +void +GCSchedulingTunables::setHighFrequencyHighLimit(uint64_t newLimit) +{ + highFrequencyHighLimitBytes_ = newLimit; + if (highFrequencyHighLimitBytes_ <= highFrequencyLowLimitBytes_) + highFrequencyLowLimitBytes_ = highFrequencyHighLimitBytes_ - 1; + MOZ_ASSERT(highFrequencyHighLimitBytes_ > highFrequencyLowLimitBytes_); +} + +void +GCSchedulingTunables::setMinEmptyChunkCount(uint32_t value) +{ + minEmptyChunkCount_ = value; + if (minEmptyChunkCount_ > maxEmptyChunkCount_) + maxEmptyChunkCount_ = minEmptyChunkCount_; + MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_); +} + +void +GCSchedulingTunables::setMaxEmptyChunkCount(uint32_t value) +{ + maxEmptyChunkCount_ = value; + if (minEmptyChunkCount_ > maxEmptyChunkCount_) + minEmptyChunkCount_ = maxEmptyChunkCount_; + MOZ_ASSERT(maxEmptyChunkCount_ >= minEmptyChunkCount_); +} + +static const size_t GCZoneAllocThresholdBaseDefault = 30 * 1024 * 1024; +static const float ZoneAllocThresholdFactorDefault = 0.9f; +static const float ZoneAllocThresholdFactorAvoidInterruptDefault = 0.9f; +static const size_t ZoneAllocDelayBytesDefault = 1024 * 1024; +static const bool DynamicHeapGrowthEnabledDefault = false; +static const uint64_t HighFrequencyThresholdUsecDefault = 1000000; +static const uint64_t HighFrequencyLowLimitBytesDefault = 100 * 1024 * 1024; +static const uint64_t HighFrequencyHighLimitBytesDefault = 500 * 1024 * 1024; +static const double HighFrequencyHeapGrowthMaxDefault = 3.0; +static const double HighFrequencyHeapGrowthMinDefault = 1.5; +static const double LowFrequencyHeapGrowthDefault = 1.5; +static const bool DynamicMarkSliceEnabledDefault = false; +static const bool RefreshFrameSlicesEnabledDefault = true; +static const uint32_t MinEmptyChunkCountDefault = 1; +static const uint32_t MaxEmptyChunkCountDefault = 30; + +GCSchedulingTunables::GCSchedulingTunables() + : gcMaxBytes_(0), + gcMaxNurseryBytes_(0), + gcZoneAllocThresholdBase_(GCZoneAllocThresholdBaseDefault), + zoneAllocThresholdFactor_(ZoneAllocThresholdFactorDefault), + zoneAllocThresholdFactorAvoidInterrupt_( + ZoneAllocThresholdFactorAvoidInterruptDefault), + zoneAllocDelayBytes_(ZoneAllocDelayBytesDefault), + dynamicHeapGrowthEnabled_(DynamicHeapGrowthEnabledDefault), + highFrequencyThresholdUsec_(HighFrequencyThresholdUsecDefault), + highFrequencyLowLimitBytes_(HighFrequencyLowLimitBytesDefault), + highFrequencyHighLimitBytes_(HighFrequencyHighLimitBytesDefault), + highFrequencyHeapGrowthMax_(HighFrequencyHeapGrowthMaxDefault), + highFrequencyHeapGrowthMin_(HighFrequencyHeapGrowthMinDefault), + lowFrequencyHeapGrowth_(LowFrequencyHeapGrowthDefault), + dynamicMarkSliceEnabled_(DynamicMarkSliceEnabledDefault), + refreshFrameSlicesEnabled_(RefreshFrameSlicesEnabledDefault), + minEmptyChunkCount_(MinEmptyChunkCountDefault), + maxEmptyChunkCount_(MaxEmptyChunkCountDefault) +{} + +void +GCRuntime::resetParameter(JSGCParamKey key, AutoLockGC& lock) +{ + switch (key) { + case JSGC_MAX_MALLOC_BYTES: + setMaxMallocBytes(0xffffffff); + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) + zone->setGCMaxMallocBytes(maxMallocBytesAllocated() * 0.9); + break; + case JSGC_SLICE_TIME_BUDGET: + defaultTimeBudget_ = + static_cast(SliceBudget::UnlimitedTimeBudget); + break; + case JSGC_MARK_STACK_LIMIT: + setMarkStackLimit(size_t(-1), lock); + break; + case JSGC_MODE: + mode = JSGC_MODE_INCREMENTAL; + break; + case JSGC_COMPACTING_ENABLED: + compactingEnabled = true; + break; + default: + tunables.resetParameter(key, lock); + for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { + zone->threshold.updateAfterGC(zone->usage.gcBytes(), GC_NORMAL, + tunables, schedulingState, lock); + } + } +} + +void +GCSchedulingTunables::resetParameter(JSGCParamKey key, const AutoLockGC& lock) +{ + switch(key) { + case JSGC_MAX_BYTES: + gcMaxBytes_ = 0xffffffff; + break; + case JSGC_MAX_NURSERY_BYTES: + gcMaxNurseryBytes_ = JS::DefaultNurseryBytes; + break; + case JSGC_HIGH_FREQUENCY_TIME_LIMIT: + highFrequencyThresholdUsec_ = HighFrequencyThresholdUsecDefault; + break; + case JSGC_HIGH_FREQUENCY_LOW_LIMIT: + setHighFrequencyLowLimit(HighFrequencyLowLimitBytesDefault); + break; + case JSGC_HIGH_FREQUENCY_HIGH_LIMIT: + setHighFrequencyHighLimit(HighFrequencyHighLimitBytesDefault); + break; + case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX: + highFrequencyHeapGrowthMax_ = HighFrequencyHeapGrowthMaxDefault; + MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0); + break; + case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN: + highFrequencyHeapGrowthMin_ = HighFrequencyHeapGrowthMinDefault; + MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0); + break; + case JSGC_LOW_FREQUENCY_HEAP_GROWTH: + lowFrequencyHeapGrowth_ = LowFrequencyHeapGrowthDefault; + MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0); + break; + case JSGC_DYNAMIC_HEAP_GROWTH: + dynamicHeapGrowthEnabled_ = DynamicHeapGrowthEnabledDefault; + break; + case JSGC_DYNAMIC_MARK_SLICE: + dynamicMarkSliceEnabled_ = DynamicMarkSliceEnabledDefault; + break; + case JSGC_ALLOCATION_THRESHOLD: + gcZoneAllocThresholdBase_ = GCZoneAllocThresholdBaseDefault; + break; + case JSGC_MIN_EMPTY_CHUNK_COUNT: + setMinEmptyChunkCount(MinEmptyChunkCountDefault); + break; + case JSGC_MAX_EMPTY_CHUNK_COUNT: + setMaxEmptyChunkCount(MaxEmptyChunkCountDefault); + break; + case JSGC_REFRESH_FRAME_SLICES_ENABLED: + refreshFrameSlicesEnabled_ = RefreshFrameSlicesEnabledDefault; + break; + default: + MOZ_CRASH("Unknown GC parameter."); + } +} + uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {