# HG changeset patch # User Jon Coppeard # Date 1518431661 0 # Node ID 37c5d7afbe4b63ec796fa1a1cb4eec4cb03fe6d3 # Parent 7574e8a716f91b151928875764c4a7816fb79139 Bug 1436697 - Fix GC heap growth factor limits r=pbone diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -170,24 +170,24 @@ class GCSchedulingTunables */ ActiveThreadOrGCTaskData gcZoneAllocThresholdBase_; /* * JSGC_ALLOCATION_THRESHOLD_FACTOR * * Fraction of threshold.gcBytes() which triggers an incremental GC. */ - UnprotectedData allocThresholdFactor_; + UnprotectedData allocThresholdFactor_; /* * JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT * * The same except when doing so would interrupt an already running GC. */ - UnprotectedData allocThresholdFactorAvoidInterrupt_; + UnprotectedData allocThresholdFactorAvoidInterrupt_; /* * Number of bytes to allocate between incremental slices in GCs triggered * by the zone allocation threshold. * * This value does not have a JSGCParamKey parameter yet. */ UnprotectedData zoneAllocDelayBytes_; @@ -255,18 +255,18 @@ class GCSchedulingTunables 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_; } - float allocThresholdFactor() const { return allocThresholdFactor_; } - float allocThresholdFactorAvoidInterrupt() const { return allocThresholdFactorAvoidInterrupt_; } + double allocThresholdFactor() const { return allocThresholdFactor_; } + double allocThresholdFactorAvoidInterrupt() const { return allocThresholdFactorAvoidInterrupt_; } size_t zoneAllocDelayBytes() const { return zoneAllocDelayBytes_; } bool isDynamicHeapGrowthEnabled() const { return dynamicHeapGrowthEnabled_; } uint64_t highFrequencyThresholdUsec() const { return highFrequencyThresholdUsec_; } uint64_t highFrequencyLowLimitBytes() const { return highFrequencyLowLimitBytes_; } uint64_t highFrequencyHighLimitBytes() const { return highFrequencyHighLimitBytes_; } double highFrequencyHeapGrowthMax() const { return highFrequencyHeapGrowthMax_; } double highFrequencyHeapGrowthMin() const { return highFrequencyHeapGrowthMin_; } double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; } diff --git a/js/src/gc/Zone.h b/js/src/gc/Zone.h --- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -44,17 +44,17 @@ class ZoneHeapThreshold public: ZoneHeapThreshold() : gcHeapGrowthFactor_(3.0), gcTriggerBytes_(0) {} double gcHeapGrowthFactor() const { return gcHeapGrowthFactor_; } size_t gcTriggerBytes() const { return gcTriggerBytes_; } - double allocTrigger(bool highFrequencyGC) const; + double eagerAllocTrigger(bool highFrequencyGC) const; void updateAfterGC(size_t lastBytes, JSGCInvocationKind gckind, const GCSchedulingTunables& tunables, const GCSchedulingState& state, const AutoLockGC& lock); void updateForRemovedArena(const GCSchedulingTunables& tunables); private: static double computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes, diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -279,26 +279,26 @@ namespace TuningDefaults { /* JSGC_ALLOCATION_THRESHOLD */ static const size_t GCZoneAllocThresholdBase = 30 * 1024 * 1024; /* JSGC_MAX_MALLOC_BYTES */ static const size_t MaxMallocBytes = 128 * 1024 * 1024; /* JSGC_ALLOCATION_THRESHOLD_FACTOR */ - static const float AllocThresholdFactor = 0.9f; + static const double AllocThresholdFactor = 0.9; /* JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT */ - static const float AllocThresholdFactorAvoidInterrupt = 0.9f; + static const double AllocThresholdFactorAvoidInterrupt = 0.9; /* no parameter */ - static const float MallocThresholdGrowFactor = 1.5f; + static const double MallocThresholdGrowFactor = 1.5; /* no parameter */ - static const float MallocThresholdShrinkFactor = 0.9f; + static const double MallocThresholdShrinkFactor = 0.9; /* no parameter */ static const size_t MallocThresholdLimit = 1024 * 1024 * 1024; /* no parameter */ static const size_t ZoneAllocDelayBytes = 1024 * 1024; /* JSGC_DYNAMIC_HEAP_GROWTH */ @@ -340,16 +340,41 @@ namespace TuningDefaults { /* JSGC_MODE */ static const JSGCMode Mode = JSGC_MODE_INCREMENTAL; /* JSGC_COMPACTING_ENABLED */ static const bool CompactingEnabled = true; }}} // namespace js::gc::TuningDefaults +/* + * 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 + * JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT parameters. + */ +static const double MinAllocationThresholdFactor = 0.9; + +/* + * We may start to collect a zone before its trigger threshold is reached if + * GCRuntime::maybeGC() is called for that zone or we start collecting other + * zones. These eager threshold factors are not configurable. + */ +static const double HighFrequencyEagerAllocTriggerFactor = 0.85; +static const double LowFrequencyEagerAllocTriggerFactor = 0.9; + +/* + * Don't allow heap growth factors to be set so low that collections could + * reduce the trigger threshold. + */ +static const double MinHighFrequencyHeapGrowthFactor = + 1.0 / Min(HighFrequencyEagerAllocTriggerFactor, MinAllocationThresholdFactor); +static const double MinLowFrequencyHeapGrowthFactor = + 1.0 / Min(LowFrequencyEagerAllocTriggerFactor, MinAllocationThresholdFactor); + /* Increase the IGC marking slice time if we are in highFrequencyGC mode. */ static const int IGC_MARK_SLICE_MULTIPLIER = 2; const AllocKind gc::slotsToThingKind[] = { /* 0 */ AllocKind::OBJECT0, AllocKind::OBJECT2, AllocKind::OBJECT2, AllocKind::OBJECT4, /* 4 */ AllocKind::OBJECT4, AllocKind::OBJECT8, AllocKind::OBJECT8, AllocKind::OBJECT8, /* 8 */ AllocKind::OBJECT8, AllocKind::OBJECT12, AllocKind::OBJECT12, AllocKind::OBJECT12, /* 12 */ AllocKind::OBJECT12, AllocKind::OBJECT16, AllocKind::OBJECT16, AllocKind::OBJECT16, @@ -1322,55 +1347,59 @@ GCSchedulingTunables::setParameter(JSGCP uint64_t newLimit = (uint64_t)value * 1024 * 1024; if (newLimit == 0) return false; setHighFrequencyHighLimit(newLimit); break; } case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX: { double newGrowth = value / 100.0; - if (newGrowth <= 0.85 || newGrowth > MaxHeapGrowthFactor) + if (newGrowth < MinHighFrequencyHeapGrowthFactor || newGrowth > MaxHeapGrowthFactor) return false; setHighFrequencyHeapGrowthMax(newGrowth); break; } case JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN: { double newGrowth = value / 100.0; - if (newGrowth <= 0.85 || newGrowth > MaxHeapGrowthFactor) + if (newGrowth < MinHighFrequencyHeapGrowthFactor || newGrowth > MaxHeapGrowthFactor) return false; setHighFrequencyHeapGrowthMin(newGrowth); break; } case JSGC_LOW_FREQUENCY_HEAP_GROWTH: { double newGrowth = value / 100.0; - if (newGrowth <= 0.9 || newGrowth > MaxHeapGrowthFactor) + if (newGrowth < MinLowFrequencyHeapGrowthFactor || newGrowth > MaxHeapGrowthFactor) return false; setLowFrequencyHeapGrowth(newGrowth); break; } case JSGC_DYNAMIC_HEAP_GROWTH: dynamicHeapGrowthEnabled_ = value != 0; break; case JSGC_DYNAMIC_MARK_SLICE: dynamicMarkSliceEnabled_ = value != 0; break; case JSGC_ALLOCATION_THRESHOLD: gcZoneAllocThresholdBase_ = value * 1024 * 1024; break; case JSGC_ALLOCATION_THRESHOLD_FACTOR: { - float newFactor = value / 100.0; - if (newFactor <= 0.1 || newFactor > 1.0) + double newFactor = value / 100.0; + if (newFactor < MinAllocationThresholdFactor || newFactor > 1.0) { + fprintf(stderr, "alloc factor %f %f\n", newFactor, MinAllocationThresholdFactor); return false; + } allocThresholdFactor_ = newFactor; break; } case JSGC_ALLOCATION_THRESHOLD_FACTOR_AVOID_INTERRUPT: { - float newFactor = value / 100.0; - if (newFactor <= 0.1 || newFactor > 1.0) + double newFactor = value / 100.0; + if (newFactor < MinAllocationThresholdFactor || newFactor > 1.0) { + fprintf(stderr, "alloc factor %f %f\n", newFactor, MinAllocationThresholdFactor); return false; + } allocThresholdFactorAvoidInterrupt_ = newFactor; break; } case JSGC_MIN_EMPTY_CHUNK_COUNT: setMinEmptyChunkCount(value); break; case JSGC_MAX_EMPTY_CHUNK_COUNT: setMaxEmptyChunkCount(value); @@ -1410,35 +1439,35 @@ GCSchedulingTunables::setHighFrequencyHi } void GCSchedulingTunables::setHighFrequencyHeapGrowthMin(double value) { highFrequencyHeapGrowthMin_ = value; if (highFrequencyHeapGrowthMin_ > highFrequencyHeapGrowthMax_) highFrequencyHeapGrowthMax_ = highFrequencyHeapGrowthMin_; - MOZ_ASSERT(highFrequencyHeapGrowthMin_ / 0.85 > 1.0); + MOZ_ASSERT(highFrequencyHeapGrowthMin_ >= MinHighFrequencyHeapGrowthFactor); MOZ_ASSERT(highFrequencyHeapGrowthMin_ <= highFrequencyHeapGrowthMax_); } void GCSchedulingTunables::setHighFrequencyHeapGrowthMax(double value) { highFrequencyHeapGrowthMax_ = value; if (highFrequencyHeapGrowthMax_ < highFrequencyHeapGrowthMin_) highFrequencyHeapGrowthMin_ = highFrequencyHeapGrowthMax_; - MOZ_ASSERT(highFrequencyHeapGrowthMax_ / 0.85 > 1.0); + MOZ_ASSERT(highFrequencyHeapGrowthMin_ >= MinHighFrequencyHeapGrowthFactor); MOZ_ASSERT(highFrequencyHeapGrowthMin_ <= highFrequencyHeapGrowthMax_); } void GCSchedulingTunables::setLowFrequencyHeapGrowth(double value) { lowFrequencyHeapGrowth_ = value; - MOZ_ASSERT(lowFrequencyHeapGrowth_ / 0.9 > 1.0); + MOZ_ASSERT(lowFrequencyHeapGrowth_ >= MinLowFrequencyHeapGrowthFactor); } void GCSchedulingTunables::setMinEmptyChunkCount(uint32_t value) { minEmptyChunkCount_ = value; if (minEmptyChunkCount_ > maxEmptyChunkCount_) maxEmptyChunkCount_ = minEmptyChunkCount_; @@ -1835,19 +1864,21 @@ GCRuntime::setMaxMallocBytes(size_t valu { tunables.setMaxMallocBytes(value); mallocCounter.setMax(value, lock); for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) zone->setGCMaxMallocBytes(value, lock); } double -ZoneHeapThreshold::allocTrigger(bool highFrequencyGC) const -{ - return (highFrequencyGC ? 0.85 : 0.9) * gcTriggerBytes(); +ZoneHeapThreshold::eagerAllocTrigger(bool highFrequencyGC) const +{ + double eagerTriggerFactor = highFrequencyGC ? HighFrequencyEagerAllocTriggerFactor + : LowFrequencyEagerAllocTriggerFactor; + return eagerTriggerFactor * gcTriggerBytes(); } /* static */ double ZoneHeapThreshold::computeZoneHeapGrowthFactorForHeapSize(size_t lastBytes, const GCSchedulingTunables& tunables, const GCSchedulingState& state) { if (!tunables.isDynamicHeapGrowthEnabled()) @@ -3282,17 +3313,17 @@ GCRuntime::maybeAllocTriggerZoneGC(Zone* if (usedBytes >= thresholdBytes) { // The threshold has been surpassed, immediately trigger a GC, which // will be done non-incrementally. triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER, usedBytes, thresholdBytes); return; } bool wouldInterruptCollection = isIncrementalGCInProgress() && !zone->isCollecting(); - float zoneGCThresholdFactor = + double zoneGCThresholdFactor = wouldInterruptCollection ? tunables.allocThresholdFactorAvoidInterrupt() : tunables.allocThresholdFactor(); size_t igcThresholdBytes = thresholdBytes * zoneGCThresholdFactor; if (usedBytes >= igcThresholdBytes) { // Reduce the delay to the start of the next incremental slice. if (zone->gcDelayBytes < ArenaSize) @@ -3361,17 +3392,17 @@ GCRuntime::maybeGC(Zone* zone) gc(GC_NORMAL, JS::gcreason::DEBUG_GC); return; } #endif if (gcIfRequested()) return; - double threshold = zone->threshold.allocTrigger(schedulingState.inHighFrequencyGCMode()); + double threshold = zone->threshold.eagerAllocTrigger(schedulingState.inHighFrequencyGCMode()); double usedBytes = zone->usage.gcBytes(); if (usedBytes > 1024 * 1024 && usedBytes >= threshold && !isIncrementalGCInProgress() && !isBackgroundSweeping()) { stats().recordTrigger(usedBytes, threshold); PrepareZoneForGC(zone); startGC(GC_NORMAL, JS::gcreason::EAGER_ALLOC_TRIGGER); } @@ -7266,21 +7297,19 @@ class AutoScheduleZonesForGC zone->scheduleGC(); // To avoid resets, continue to collect any zones that were being // collected in a previous slice. if (rt->gc.isIncrementalGCInProgress() && zone->wasGCStarted()) zone->scheduleGC(); // This is a heuristic to reduce the total number of collections. - if (zone->usage.gcBytes() >= - zone->threshold.allocTrigger(rt->gc.schedulingState.inHighFrequencyGCMode())) - { + bool inHighFrequencyMode = rt->gc.schedulingState.inHighFrequencyGCMode(); + if (zone->usage.gcBytes() >= zone->threshold.eagerAllocTrigger(inHighFrequencyMode)) zone->scheduleGC(); - } // This ensures we collect zones that have reached the malloc limit. if (zone->shouldTriggerGCForTooMuchMalloc()) zone->scheduleGC(); } } ~AutoScheduleZonesForGC() { @@ -8833,17 +8862,17 @@ ZoneGCTriggerBytesGetter(JSContext* cx, return true; } static bool ZoneGCAllocTriggerGetter(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); bool highFrequency = cx->runtime()->gc.schedulingState.inHighFrequencyGCMode(); - args.rval().setNumber(double(cx->zone()->threshold.allocTrigger(highFrequency))); + args.rval().setNumber(double(cx->zone()->threshold.eagerAllocTrigger(highFrequency))); return true; } static bool ZoneMallocBytesGetter(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); args.rval().setNumber(double(cx->zone()->GCMallocBytes()));