Backed out 7 changesets (bug 1833854) for causing bustages on Scheduling.cpp CLOSED TREE

Backed out changeset 503290081afb (bug 1833854)
Backed out changeset fccbd85a653c (bug 1833854)
Backed out changeset 6629a120ed76 (bug 1833854)
Backed out changeset 1545749d5317 (bug 1833854)
Backed out changeset ce5f0d5ba79c (bug 1833854)
Backed out changeset 0f2110cf713c (bug 1833854)
Backed out changeset e81efd1cfa8d (bug 1833854)
This commit is contained in:
Norisz Fay 2023-05-20 12:06:11 +03:00
parent c950d9c496
commit 34dd3fd540
8 changed files with 787 additions and 472 deletions

View file

@ -324,6 +324,15 @@ typedef enum JSGCParamKey {
*/
JSGC_PRETENURE_THRESHOLD = 28,
/**
* 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).

View file

@ -1187,6 +1187,14 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key) {
uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
switch (key) {
case JSGC_MAX_BYTES:
return uint32_t(tunables.gcMaxBytes());
case JSGC_MIN_NURSERY_BYTES:
MOZ_ASSERT(tunables.gcMinNurseryBytes() < UINT32_MAX);
return uint32_t(tunables.gcMinNurseryBytes());
case JSGC_MAX_NURSERY_BYTES:
MOZ_ASSERT(tunables.gcMaxNurseryBytes() < UINT32_MAX);
return uint32_t(tunables.gcMaxNurseryBytes());
case JSGC_BYTES:
return uint32_t(heapSize.bytes());
case JSGC_NURSERY_BYTES:
@ -1210,6 +1218,28 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
MOZ_RELEASE_ASSERT(defaultTimeBudgetMS_ >= 0);
MOZ_RELEASE_ASSERT(defaultTimeBudgetMS_ <= UINT32_MAX);
return uint32_t(defaultTimeBudgetMS_);
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
return tunables.highFrequencyThreshold().ToMilliseconds();
case JSGC_SMALL_HEAP_SIZE_MAX:
return tunables.smallHeapSizeMaxBytes() / 1024 / 1024;
case JSGC_LARGE_HEAP_SIZE_MIN:
return tunables.largeHeapSizeMinBytes() / 1024 / 1024;
case JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH:
return uint32_t(tunables.highFrequencySmallHeapGrowth() * 100);
case JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH:
return uint32_t(tunables.highFrequencyLargeHeapGrowth() * 100);
case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
return uint32_t(tunables.lowFrequencyHeapGrowth() * 100);
case JSGC_BALANCED_HEAP_LIMITS_ENABLED:
return uint32_t(tunables.balancedHeapLimitsEnabled());
case JSGC_HEAP_GROWTH_FACTOR:
return uint32_t(tunables.heapGrowthFactor());
case JSGC_ALLOCATION_THRESHOLD:
return tunables.gcZoneAllocThresholdBase() / 1024 / 1024;
case JSGC_SMALL_HEAP_INCREMENTAL_LIMIT:
return uint32_t(tunables.smallHeapIncrementalLimit() * 100);
case JSGC_LARGE_HEAP_INCREMENTAL_LIMIT:
return uint32_t(tunables.largeHeapIncrementalLimit() * 100);
case JSGC_MIN_EMPTY_CHUNK_COUNT:
return minEmptyChunkCount(lock);
case JSGC_MAX_EMPTY_CHUNK_COUNT:
@ -1220,6 +1250,31 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
return parallelMarkingEnabled;
case JSGC_INCREMENTAL_WEAKMAP_ENABLED:
return marker().incrementalWeakMapMarkingEnabled;
case JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION:
return tunables.nurseryFreeThresholdForIdleCollection();
case JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT:
return uint32_t(tunables.nurseryFreeThresholdForIdleCollectionFraction() *
100.0f);
case JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS:
return tunables.nurseryTimeoutForIdleCollection().ToMilliseconds();
case JSGC_PRETENURE_THRESHOLD:
return uint32_t(tunables.pretenureThreshold() * 100);
case JSGC_PRETENURE_GROUP_THRESHOLD:
return tunables.pretenureGroupThreshold();
case JSGC_PRETENURE_STRING_THRESHOLD:
return uint32_t(tunables.pretenureStringThreshold() * 100);
case JSGC_STOP_PRETENURE_STRING_THRESHOLD:
return uint32_t(tunables.stopPretenureStringThreshold() * 100);
case JSGC_MIN_LAST_DITCH_GC_PERIOD:
return tunables.minLastDitchGCPeriod().ToSeconds();
case JSGC_ZONE_ALLOC_DELAY_KB:
return tunables.zoneAllocDelayBytes() / 1024;
case JSGC_MALLOC_THRESHOLD_BASE:
return tunables.mallocThresholdBase() / 1024 / 1024;
case JSGC_URGENT_THRESHOLD_MB:
return tunables.urgentThresholdBytes() / 1024 / 1024;
case JSGC_PARALLEL_MARKING_THRESHOLD_KB:
return tunables.parallelMarkingThresholdBytes() / 1024;
case JSGC_CHUNK_BYTES:
return ChunkSize;
case JSGC_HELPER_THREAD_RATIO:
@ -1235,7 +1290,7 @@ uint32_t GCRuntime::getParameter(JSGCParamKey key, const AutoLockGC& lock) {
case JSGC_SYSTEM_PAGE_SIZE_KB:
return SystemPageSize() / 1024;
default:
return tunables.getParameter(key);
MOZ_CRASH("Unknown parameter key");
}
}

View file

@ -75,6 +75,7 @@ class TenuredChunk;
_("nurseryTimeoutForIdleCollectionMS", \
JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS, true) \
_("pretenureThreshold", JSGC_PRETENURE_THRESHOLD, true) \
_("pretenureGroupThreshold", JSGC_PRETENURE_GROUP_THRESHOLD, true) \
_("zoneAllocDelayKB", JSGC_ZONE_ALLOC_DELAY_KB, true) \
_("mallocThresholdBase", JSGC_MALLOC_THRESHOLD_BASE, true) \
_("urgentThreshold", JSGC_URGENT_THRESHOLD_MB, true) \

View file

@ -1760,7 +1760,6 @@ void js::Nursery::maybeResizeNursery(JS::GCOptions options,
tunables().gcMaxNurseryBytes());
MOZ_ASSERT(roundSize(newCapacity) == newCapacity);
MOZ_ASSERT(newCapacity >= SystemPageSize());
if (newCapacity > capacity()) {
growAllocableSpace(newCapacity);
@ -1893,7 +1892,11 @@ void js::Nursery::clearRecentGrowthData() {
/* static */
size_t js::Nursery::roundSize(size_t size) {
size_t step = size >= ChunkSize ? ChunkSize : SystemPageSize();
return Round(size, step);
size = Round(size, step);
MOZ_ASSERT(size >= SystemPageSize());
return size;
}
void js::Nursery::growAllocableSpace(size_t newCapacity) {

View file

@ -7,7 +7,6 @@
#include "gc/Scheduling.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/TimeStamp.h"
#include <algorithm>
@ -24,8 +23,6 @@ using namespace js;
using namespace js::gc;
using mozilla::CheckedInt;
using mozilla::Maybe;
using mozilla::Nothing;
using mozilla::Some;
using mozilla::TimeDuration;
using mozilla::TimeStamp;
@ -46,231 +43,386 @@ static constexpr double MinHeapGrowthFactor =
1.0f / std::min(HighFrequencyEagerAllocTriggerFactor,
LowFrequencyEagerAllocTriggerFactor);
// Limit various parameters to reasonable levels to catch errors.
static constexpr double MaxHeapGrowthFactor = 100;
static constexpr size_t MaxNurseryBytesParam = 128 * 1024 * 1024;
namespace {
// Helper classes to marshal GC parameter values to/from uint32_t.
template <typename T>
struct ConvertGeneric {
static uint32_t toUint32(T value) {
MOZ_ASSERT(value >= 0 && value <= UINT32_MAX);
return uint32_t(value);
}
static Maybe<T> fromUint32(uint32_t param) {
// Currently we use explicit conversion and don't range check.
return Some(T(param));
}
};
using ConvertBool = ConvertGeneric<bool>;
using ConvertSize = ConvertGeneric<size_t>;
using ConvertDouble = ConvertGeneric<double>;
struct ConvertTimes100 {
static uint32_t toUint32(double value) { return uint32_t(value * 100.0); }
static Maybe<double> fromUint32(uint32_t param) {
return Some(double(param) / 100.0);
}
};
struct ConvertNurseryBytes : ConvertSize {
static Maybe<size_t> fromUint32(uint32_t param) {
return Some(Nursery::roundSize(param));
}
};
struct ConvertKB {
static uint32_t toUint32(size_t value) { return value / 1024; }
static Maybe<size_t> fromUint32(uint32_t param) {
// Parameters which represent heap sizes in bytes are restricted to values
// which can be represented on 32 bit platforms.
CheckedInt<uint32_t> size = CheckedInt<uint32_t>(param) * 1024;
return size.isValid() ? Some(size_t(size.value())) : Nothing();
}
};
struct ConvertMB {
static uint32_t toUint32(size_t value) { return value / (1024 * 1024); }
static Maybe<size_t> fromUint32(uint32_t param) {
// Parameters which represent heap sizes in bytes are restricted to values
// which can be represented on 32 bit platforms.
CheckedInt<uint32_t> size = CheckedInt<uint32_t>(param) * 1024 * 1024;
return size.isValid() ? Some(size_t(size.value())) : Nothing();
}
};
struct ConvertMillis {
static uint32_t toUint32(TimeDuration value) {
return uint32_t(value.ToMilliseconds());
}
static Maybe<TimeDuration> fromUint32(uint32_t param) {
return Some(TimeDuration::FromMilliseconds(param));
}
};
struct ConvertSeconds {
static uint32_t toUint32(TimeDuration value) {
return uint32_t(value.ToSeconds());
}
static Maybe<TimeDuration> fromUint32(uint32_t param) {
return Some(TimeDuration::FromSeconds(param));
}
};
} // anonymous namespace
// Helper functions to check GC parameter values
template <typename T>
static bool NoCheck(T value) {
return true;
}
template <typename T>
static bool CheckNonZero(T value) {
return value != 0;
}
static bool CheckNurserySize(size_t bytes) {
return bytes >= SystemPageSize() && bytes <= MaxNurseryBytesParam;
}
static bool CheckHeapGrowth(double growth) {
return growth >= MinHeapGrowthFactor && growth <= MaxHeapGrowthFactor;
}
static bool CheckIncrementalLimit(double factor) {
return factor >= 1.0 && factor <= MaxHeapGrowthFactor;
}
static bool CheckNonZeroUnitRange(double value) {
return value > 0.0 && value <= 100.0;
}
GCSchedulingTunables::GCSchedulingTunables() {
#define INIT_TUNABLE_FIELD(key, type, name, convert, check, default) \
name##_ = default; \
MOZ_ASSERT(check(name##_));
FOR_EACH_GC_TUNABLE(INIT_TUNABLE_FIELD)
#undef INIT_TUNABLE_FIELD
checkInvariants();
}
uint32_t GCSchedulingTunables::getParameter(JSGCParamKey key) {
switch (key) {
#define GET_TUNABLE_FIELD(key, type, name, convert, check, default) \
case key: \
return convert::toUint32(name##_);
FOR_EACH_GC_TUNABLE(GET_TUNABLE_FIELD)
#undef GET_TUNABLE_FIELD
default:
MOZ_CRASH("Unknown parameter key");
}
}
GCSchedulingTunables::GCSchedulingTunables()
: gcMaxBytes_(TuningDefaults::GCMaxBytes),
gcMinNurseryBytes_(Nursery::roundSize(TuningDefaults::GCMinNurseryBytes)),
gcMaxNurseryBytes_(Nursery::roundSize(JS::DefaultNurseryMaxBytes)),
gcZoneAllocThresholdBase_(TuningDefaults::GCZoneAllocThresholdBase),
smallHeapIncrementalLimit_(TuningDefaults::SmallHeapIncrementalLimit),
largeHeapIncrementalLimit_(TuningDefaults::LargeHeapIncrementalLimit),
zoneAllocDelayBytes_(TuningDefaults::ZoneAllocDelayBytes),
highFrequencyThreshold_(
TimeDuration::FromSeconds(TuningDefaults::HighFrequencyThreshold)),
smallHeapSizeMaxBytes_(TuningDefaults::SmallHeapSizeMaxBytes),
largeHeapSizeMinBytes_(TuningDefaults::LargeHeapSizeMinBytes),
highFrequencySmallHeapGrowth_(
TuningDefaults::HighFrequencySmallHeapGrowth),
highFrequencyLargeHeapGrowth_(
TuningDefaults::HighFrequencyLargeHeapGrowth),
lowFrequencyHeapGrowth_(TuningDefaults::LowFrequencyHeapGrowth),
balancedHeapLimitsEnabled_(TuningDefaults::BalancedHeapLimitsEnabled),
heapGrowthFactor_(TuningDefaults::HeapGrowthFactor),
nurseryFreeThresholdForIdleCollection_(
TuningDefaults::NurseryFreeThresholdForIdleCollection),
nurseryFreeThresholdForIdleCollectionFraction_(
TuningDefaults::NurseryFreeThresholdForIdleCollectionFraction),
nurseryTimeoutForIdleCollection_(TimeDuration::FromMilliseconds(
TuningDefaults::NurseryTimeoutForIdleCollectionMS)),
pretenureThreshold_(TuningDefaults::PretenureThreshold),
pretenureGroupThreshold_(TuningDefaults::PretenureGroupThreshold),
pretenureStringThreshold_(TuningDefaults::PretenureStringThreshold),
stopPretenureStringThreshold_(
TuningDefaults::StopPretenureStringThreshold),
minLastDitchGCPeriod_(
TimeDuration::FromSeconds(TuningDefaults::MinLastDitchGCPeriod)),
mallocThresholdBase_(TuningDefaults::MallocThresholdBase),
urgentThresholdBytes_(TuningDefaults::UrgentThresholdBytes),
parallelMarkingThresholdBytes_(
TuningDefaults::ParallelMarkingThresholdBytes) {}
bool GCSchedulingTunables::setParameter(JSGCParamKey key, uint32_t value) {
auto guard = mozilla::MakeScopeExit([this] { checkInvariants(); });
// Limit various parameters to reasonable levels to catch errors.
const double MaxHeapGrowthFactor = 100;
const size_t MaxNurseryBytesParam = 128 * 1024 * 1024;
switch (key) {
#define SET_TUNABLE_FIELD(key, type, name, convert, check, default) \
case key: { \
Maybe<type> converted = convert::fromUint32(value); \
if (!converted || !check(converted.value())) { \
return false; \
} \
name##_ = converted.value(); \
break; \
}
FOR_EACH_GC_TUNABLE(SET_TUNABLE_FIELD)
#undef SET_TUNABLE_FIELD
case JSGC_MAX_BYTES:
gcMaxBytes_ = value;
break;
case JSGC_MIN_NURSERY_BYTES:
if (value < SystemPageSize() || value >= MaxNurseryBytesParam) {
return false;
}
value = Nursery::roundSize(value);
if (value > gcMaxNurseryBytes_) {
return false;
}
gcMinNurseryBytes_ = value;
break;
case JSGC_MAX_NURSERY_BYTES:
if (value < SystemPageSize() || value >= MaxNurseryBytesParam) {
return false;
}
value = Nursery::roundSize(value);
if (value < gcMinNurseryBytes_) {
return false;
}
gcMaxNurseryBytes_ = value;
break;
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
highFrequencyThreshold_ = TimeDuration::FromMilliseconds(value);
break;
case JSGC_SMALL_HEAP_SIZE_MAX: {
size_t newLimit;
if (!megabytesToBytes(value, &newLimit)) {
return false;
}
setSmallHeapSizeMaxBytes(newLimit);
break;
}
case JSGC_LARGE_HEAP_SIZE_MIN: {
size_t newLimit;
if (!megabytesToBytes(value, &newLimit) || newLimit == 0) {
return false;
}
setLargeHeapSizeMinBytes(newLimit);
break;
}
case JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH: {
double newGrowth = value / 100.0;
if (newGrowth < MinHeapGrowthFactor || newGrowth > MaxHeapGrowthFactor) {
return false;
}
setHighFrequencySmallHeapGrowth(newGrowth);
break;
}
case JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH: {
double newGrowth = value / 100.0;
if (newGrowth < MinHeapGrowthFactor || newGrowth > MaxHeapGrowthFactor) {
return false;
}
setHighFrequencyLargeHeapGrowth(newGrowth);
break;
}
case JSGC_BALANCED_HEAP_LIMITS_ENABLED: {
balancedHeapLimitsEnabled_ = bool(value);
break;
}
case JSGC_LOW_FREQUENCY_HEAP_GROWTH: {
double newGrowth = value / 100.0;
if (newGrowth < MinHeapGrowthFactor || newGrowth > MaxHeapGrowthFactor) {
return false;
}
setLowFrequencyHeapGrowth(newGrowth);
break;
}
case JSGC_HEAP_GROWTH_FACTOR: {
setHeapGrowthFactor(double(value));
break;
}
case JSGC_ALLOCATION_THRESHOLD: {
size_t threshold;
if (!megabytesToBytes(value, &threshold)) {
return false;
}
gcZoneAllocThresholdBase_ = threshold;
break;
}
case JSGC_SMALL_HEAP_INCREMENTAL_LIMIT: {
double newFactor = value / 100.0;
if (newFactor < 1.0f || newFactor > MaxHeapGrowthFactor) {
return false;
}
smallHeapIncrementalLimit_ = newFactor;
break;
}
case JSGC_LARGE_HEAP_INCREMENTAL_LIMIT: {
double newFactor = value / 100.0;
if (newFactor < 1.0f || newFactor > MaxHeapGrowthFactor) {
return false;
}
largeHeapIncrementalLimit_ = newFactor;
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.0;
break;
case JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS:
nurseryTimeoutForIdleCollection_ = TimeDuration::FromMilliseconds(value);
break;
case JSGC_PRETENURE_THRESHOLD: {
// 100 disables pretenuring
if (value == 0 || value > 100) {
return false;
}
pretenureThreshold_ = value / 100.0;
break;
}
case JSGC_PRETENURE_GROUP_THRESHOLD:
if (value <= 0) {
return false;
}
pretenureGroupThreshold_ = value;
break;
case JSGC_PRETENURE_STRING_THRESHOLD:
// 100 disables pretenuring
if (value == 0 || value > 100) {
return false;
}
pretenureStringThreshold_ = value / 100.0;
break;
case JSGC_STOP_PRETENURE_STRING_THRESHOLD:
if (value == 0 || value > 100) {
return false;
}
stopPretenureStringThreshold_ = value / 100.0;
break;
case JSGC_MIN_LAST_DITCH_GC_PERIOD:
minLastDitchGCPeriod_ = TimeDuration::FromSeconds(value);
break;
case JSGC_ZONE_ALLOC_DELAY_KB: {
size_t delay;
if (!kilobytesToBytes(value, &delay) || delay == 0) {
return false;
}
zoneAllocDelayBytes_ = delay;
break;
}
case JSGC_MALLOC_THRESHOLD_BASE: {
size_t threshold;
if (!megabytesToBytes(value, &threshold)) {
return false;
}
mallocThresholdBase_ = threshold;
break;
}
case JSGC_URGENT_THRESHOLD_MB: {
size_t threshold;
if (!megabytesToBytes(value, &threshold)) {
return false;
}
urgentThresholdBytes_ = threshold;
break;
}
case JSGC_PARALLEL_MARKING_THRESHOLD_KB: {
size_t threshold;
if (!kilobytesToBytes(value, &threshold)) {
return false;
}
parallelMarkingThresholdBytes_ = threshold;
break;
}
default:
MOZ_CRASH("Unknown GC parameter.");
}
maintainInvariantsAfterUpdate(key);
return true;
}
/* static */
bool GCSchedulingTunables::megabytesToBytes(uint32_t value, size_t* bytesOut) {
MOZ_ASSERT(bytesOut);
// Parameters which represent heap sizes in bytes are restricted to values
// which can be represented on 32 bit platforms.
CheckedInt<uint32_t> size = CheckedInt<uint32_t>(value) * 1024 * 1024;
if (!size.isValid()) {
return false;
}
*bytesOut = size.value();
return true;
}
/* static */
bool GCSchedulingTunables::kilobytesToBytes(uint32_t value, size_t* bytesOut) {
MOZ_ASSERT(bytesOut);
CheckedInt<size_t> size = CheckedInt<size_t>(value) * 1024;
if (!size.isValid()) {
return false;
}
*bytesOut = size.value();
return true;
}
void GCSchedulingTunables::setSmallHeapSizeMaxBytes(size_t value) {
smallHeapSizeMaxBytes_ = value;
if (smallHeapSizeMaxBytes_ >= largeHeapSizeMinBytes_) {
largeHeapSizeMinBytes_ = smallHeapSizeMaxBytes_ + 1;
}
MOZ_ASSERT(largeHeapSizeMinBytes_ > smallHeapSizeMaxBytes_);
}
void GCSchedulingTunables::setLargeHeapSizeMinBytes(size_t value) {
largeHeapSizeMinBytes_ = value;
if (largeHeapSizeMinBytes_ <= smallHeapSizeMaxBytes_) {
smallHeapSizeMaxBytes_ = largeHeapSizeMinBytes_ - 1;
}
MOZ_ASSERT(largeHeapSizeMinBytes_ > smallHeapSizeMaxBytes_);
}
void GCSchedulingTunables::setHighFrequencyLargeHeapGrowth(double value) {
highFrequencyLargeHeapGrowth_ = value;
if (highFrequencyLargeHeapGrowth_ > highFrequencySmallHeapGrowth_) {
highFrequencySmallHeapGrowth_ = highFrequencyLargeHeapGrowth_;
}
MOZ_ASSERT(highFrequencyLargeHeapGrowth_ >= MinHeapGrowthFactor);
MOZ_ASSERT(highFrequencyLargeHeapGrowth_ <= highFrequencySmallHeapGrowth_);
}
void GCSchedulingTunables::setHighFrequencySmallHeapGrowth(double value) {
highFrequencySmallHeapGrowth_ = value;
if (highFrequencySmallHeapGrowth_ < highFrequencyLargeHeapGrowth_) {
highFrequencyLargeHeapGrowth_ = highFrequencySmallHeapGrowth_;
}
MOZ_ASSERT(highFrequencyLargeHeapGrowth_ >= MinHeapGrowthFactor);
MOZ_ASSERT(highFrequencyLargeHeapGrowth_ <= highFrequencySmallHeapGrowth_);
}
void GCSchedulingTunables::setLowFrequencyHeapGrowth(double value) {
lowFrequencyHeapGrowth_ = value;
MOZ_ASSERT(lowFrequencyHeapGrowth_ >= MinHeapGrowthFactor);
}
void GCSchedulingTunables::setHeapGrowthFactor(double value) {
heapGrowthFactor_ = value;
}
void GCSchedulingTunables::resetParameter(JSGCParamKey key) {
auto guard = mozilla::MakeScopeExit([this] { checkInvariants(); });
switch (key) {
#define RESET_TUNABLE_FIELD(key, type, name, convert, check, default) \
case key: \
name##_ = default; \
MOZ_ASSERT(check(name##_)); \
break;
FOR_EACH_GC_TUNABLE(RESET_TUNABLE_FIELD)
#undef RESET_TUNABLE_FIELD
case JSGC_MAX_BYTES:
gcMaxBytes_ = TuningDefaults::GCMaxBytes;
break;
case JSGC_MIN_NURSERY_BYTES:
case JSGC_MAX_NURSERY_BYTES:
// Reset these togeather to maintain their min <= max invariant.
gcMinNurseryBytes_ = TuningDefaults::GCMinNurseryBytes;
gcMaxNurseryBytes_ = JS::DefaultNurseryMaxBytes;
break;
case JSGC_HIGH_FREQUENCY_TIME_LIMIT:
highFrequencyThreshold_ =
TimeDuration::FromSeconds(TuningDefaults::HighFrequencyThreshold);
break;
case JSGC_SMALL_HEAP_SIZE_MAX:
setSmallHeapSizeMaxBytes(TuningDefaults::SmallHeapSizeMaxBytes);
break;
case JSGC_LARGE_HEAP_SIZE_MIN:
setLargeHeapSizeMinBytes(TuningDefaults::LargeHeapSizeMinBytes);
break;
case JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH:
setHighFrequencySmallHeapGrowth(
TuningDefaults::HighFrequencySmallHeapGrowth);
break;
case JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH:
setHighFrequencyLargeHeapGrowth(
TuningDefaults::HighFrequencyLargeHeapGrowth);
break;
case JSGC_LOW_FREQUENCY_HEAP_GROWTH:
setLowFrequencyHeapGrowth(TuningDefaults::LowFrequencyHeapGrowth);
break;
case JSGC_BALANCED_HEAP_LIMITS_ENABLED:
balancedHeapLimitsEnabled_ = TuningDefaults::BalancedHeapLimitsEnabled;
break;
case JSGC_HEAP_GROWTH_FACTOR:
setHeapGrowthFactor(TuningDefaults::HeapGrowthFactor);
break;
case JSGC_ALLOCATION_THRESHOLD:
gcZoneAllocThresholdBase_ = TuningDefaults::GCZoneAllocThresholdBase;
break;
case JSGC_SMALL_HEAP_INCREMENTAL_LIMIT:
smallHeapIncrementalLimit_ = TuningDefaults::SmallHeapIncrementalLimit;
break;
case JSGC_LARGE_HEAP_INCREMENTAL_LIMIT:
largeHeapIncrementalLimit_ = TuningDefaults::LargeHeapIncrementalLimit;
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_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS:
nurseryTimeoutForIdleCollection_ = TimeDuration::FromMilliseconds(
TuningDefaults::NurseryTimeoutForIdleCollectionMS);
break;
case JSGC_PRETENURE_THRESHOLD:
pretenureThreshold_ = TuningDefaults::PretenureThreshold;
break;
case JSGC_PRETENURE_GROUP_THRESHOLD:
pretenureGroupThreshold_ = TuningDefaults::PretenureGroupThreshold;
break;
case JSGC_PRETENURE_STRING_THRESHOLD:
pretenureStringThreshold_ = TuningDefaults::PretenureStringThreshold;
break;
case JSGC_MIN_LAST_DITCH_GC_PERIOD:
minLastDitchGCPeriod_ =
TimeDuration::FromSeconds(TuningDefaults::MinLastDitchGCPeriod);
break;
case JSGC_MALLOC_THRESHOLD_BASE:
mallocThresholdBase_ = TuningDefaults::MallocThresholdBase;
break;
case JSGC_URGENT_THRESHOLD_MB:
urgentThresholdBytes_ = TuningDefaults::UrgentThresholdBytes;
break;
case JSGC_PARALLEL_MARKING_THRESHOLD_KB:
parallelMarkingThresholdBytes_ =
TuningDefaults::ParallelMarkingThresholdBytes;
break;
default:
MOZ_CRASH("Unknown GC parameter.");
}
maintainInvariantsAfterUpdate(key);
}
void GCSchedulingTunables::maintainInvariantsAfterUpdate(JSGCParamKey updated) {
switch (updated) {
case JSGC_MIN_NURSERY_BYTES:
if (gcMaxNurseryBytes_ < gcMinNurseryBytes_) {
gcMaxNurseryBytes_ = gcMinNurseryBytes_;
}
break;
case JSGC_MAX_NURSERY_BYTES:
if (gcMinNurseryBytes_ > gcMaxNurseryBytes_) {
gcMinNurseryBytes_ = gcMaxNurseryBytes_;
}
break;
case JSGC_SMALL_HEAP_SIZE_MAX:
if (smallHeapSizeMaxBytes_ >= largeHeapSizeMinBytes_) {
largeHeapSizeMinBytes_ = smallHeapSizeMaxBytes_ + 1;
}
break;
case JSGC_LARGE_HEAP_SIZE_MIN:
if (largeHeapSizeMinBytes_ <= smallHeapSizeMaxBytes_) {
smallHeapSizeMaxBytes_ = largeHeapSizeMinBytes_ - 1;
}
break;
case JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH:
if (highFrequencySmallHeapGrowth_ < highFrequencyLargeHeapGrowth_) {
highFrequencyLargeHeapGrowth_ = highFrequencySmallHeapGrowth_;
}
break;
case JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH:
if (highFrequencyLargeHeapGrowth_ > highFrequencySmallHeapGrowth_) {
highFrequencySmallHeapGrowth_ = highFrequencyLargeHeapGrowth_;
}
break;
default:
break;
}
}
void GCSchedulingTunables::checkInvariants() {
MOZ_ASSERT(gcMinNurseryBytes_ == Nursery::roundSize(gcMinNurseryBytes_));
MOZ_ASSERT(gcMaxNurseryBytes_ == Nursery::roundSize(gcMaxNurseryBytes_));
MOZ_ASSERT(gcMinNurseryBytes_ <= gcMaxNurseryBytes_);
MOZ_ASSERT(gcMinNurseryBytes_ >= SystemPageSize());
MOZ_ASSERT(gcMaxNurseryBytes_ <= MaxNurseryBytesParam);
MOZ_ASSERT(largeHeapSizeMinBytes_ > smallHeapSizeMaxBytes_);
MOZ_ASSERT(lowFrequencyHeapGrowth_ >= MinHeapGrowthFactor);
MOZ_ASSERT(lowFrequencyHeapGrowth_ <= MaxHeapGrowthFactor);
MOZ_ASSERT(highFrequencySmallHeapGrowth_ >= MinHeapGrowthFactor);
MOZ_ASSERT(highFrequencySmallHeapGrowth_ <= MaxHeapGrowthFactor);
MOZ_ASSERT(highFrequencyLargeHeapGrowth_ <= highFrequencySmallHeapGrowth_);
MOZ_ASSERT(highFrequencyLargeHeapGrowth_ >= MinHeapGrowthFactor);
MOZ_ASSERT(highFrequencySmallHeapGrowth_ <= MaxHeapGrowthFactor);
}
void GCSchedulingState::updateHighFrequencyMode(

View file

@ -314,7 +314,6 @@
#include "mozilla/Atomics.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Maybe.h"
#include "mozilla/TimeStamp.h"
#include "gc/GCEnum.h"
#include "js/AllocPolicy.h"
@ -323,204 +322,6 @@
#include "js/HeapAPI.h"
#include "threading/ProtectedData.h"
// Macro to define scheduling tunables for GC parameters. Expands its argument
// repeatedly with the following arguments:
// - key: the JSGCParamKey value for this parameter
// - type: the storage type
// - name: the name of GCSchedulingTunables getter method
// - convert: a helper class defined in Scheduling.cpp that provides
// conversion methods
// - check: a helper function defined in Scheduling.cppto check the value is
// valid
// - default: the initial value and that assigned by resetParameter
#define FOR_EACH_GC_TUNABLE(_) \
/* \
* JSGC_MAX_BYTES \
* \
* Maximum nominal heap before last ditch GC. \
*/ \
_(JSGC_MAX_BYTES, size_t, gcMaxBytes, ConvertSize, NoCheck, 0xffffffff) \
\
/* \
* JSGC_MIN_NURSERY_BYTES \
* JSGC_MAX_NURSERY_BYTES \
* \
* Minimum and maximum nursery size for each runtime. \
*/ \
_(JSGC_MIN_NURSERY_BYTES, size_t, gcMinNurseryBytes, ConvertNurseryBytes, \
CheckNurserySize, 256 * 1024) \
_(JSGC_MAX_NURSERY_BYTES, size_t, gcMaxNurseryBytes, ConvertNurseryBytes, \
CheckNurserySize, JS::DefaultNurseryMaxBytes) \
\
/* \
* JSGC_ALLOCATION_THRESHOLD \
* \
* \
* The base value used to compute zone->threshold.bytes(). When \
* gcHeapSize.bytes() exceeds threshold.bytes() for a zone, the zone may be \
* scheduled for a GC, depending on the exact circumstances. \
*/ \
_(JSGC_ALLOCATION_THRESHOLD, size_t, gcZoneAllocThresholdBase, ConvertMB, \
NoCheck, 27 * 1024 * 1024) \
\
/* \
* JSGC_SMALL_HEAP_SIZE_MAX \
* JSGC_LARGE_HEAP_SIZE_MIN \
* \
* Used to classify heap sizes into one of small, medium or large. This \
* affects the calcuation of the incremental GC trigger and the heap growth \
* factor in high frequency GC mode. \
*/ \
_(JSGC_SMALL_HEAP_SIZE_MAX, size_t, smallHeapSizeMaxBytes, ConvertMB, \
NoCheck, 100 * 1024 * 1024) \
_(JSGC_LARGE_HEAP_SIZE_MIN, size_t, largeHeapSizeMinBytes, ConvertMB, \
CheckNonZero, 500 * 1024 * 1024) \
\
/* \
* JSGC_SMALL_HEAP_INCREMENTAL_LIMIT \
* JSGC_LARGE_HEAP_INCREMENTAL_LIMIT \
* \
* Multiple of threshold.bytes() which triggers a non-incremental GC. \
* \
* The small heap limit must be greater than 1.3 to maintain performance on \
* splay-latency. \
*/ \
_(JSGC_SMALL_HEAP_INCREMENTAL_LIMIT, double, smallHeapIncrementalLimit, \
ConvertTimes100, CheckIncrementalLimit, 1.50) \
_(JSGC_LARGE_HEAP_INCREMENTAL_LIMIT, double, largeHeapIncrementalLimit, \
ConvertTimes100, CheckIncrementalLimit, 1.10) \
\
/* \
* JSGC_HIGH_FREQUENCY_TIME_LIMIT \
* \
* We enter high-frequency mode if we GC a twice within this many \
* millisconds. \
*/ \
_(JSGC_HIGH_FREQUENCY_TIME_LIMIT, mozilla::TimeDuration, \
highFrequencyThreshold, ConvertMillis, NoCheck, \
mozilla::TimeDuration::FromSeconds(1)) \
\
/* \
* JSGC_LOW_FREQUENCY_HEAP_GROWTH \
* \
* When not in |highFrequencyGC| mode, this is the global (stored per-zone) \
* "HeapGrowthFactor". \
*/ \
_(JSGC_LOW_FREQUENCY_HEAP_GROWTH, double, lowFrequencyHeapGrowth, \
ConvertTimes100, CheckHeapGrowth, 1.5) \
\
/* \
* JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH \
* JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH \
* \
* When in the |highFrequencyGC| mode, these parameterize the per-zone \
* "HeapGrowthFactor" computation. \
*/ \
_(JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH, double, \
highFrequencySmallHeapGrowth, ConvertTimes100, CheckHeapGrowth, 3.0) \
_(JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH, double, \
highFrequencyLargeHeapGrowth, ConvertTimes100, CheckHeapGrowth, 1.5) \
\
/* \
* JSGC_MALLOC_THRESHOLD_BASE \
* \
* The base value used to compute the GC trigger for malloc allocated \
* memory. \
*/ \
_(JSGC_MALLOC_THRESHOLD_BASE, size_t, mallocThresholdBase, ConvertMB, \
NoCheck, 38 * 1024 * 1024) \
\
/* \
* Number of bytes to allocate between incremental slices in GCs triggered \
* by the zone allocation threshold. \
*/ \
_(JSGC_ZONE_ALLOC_DELAY_KB, size_t, zoneAllocDelayBytes, ConvertKB, \
CheckNonZero, 1024 * 1024) \
\
/* \
* JSGC_URGENT_THRESHOLD_MB \
* \
* The point before reaching the non-incremental limit at which to start \
* increasing the slice budget and frequency of allocation triggered slices. \
*/ \
_(JSGC_URGENT_THRESHOLD_MB, size_t, urgentThresholdBytes, ConvertMB, \
NoCheck, 16 * 1024 * 1024) \
\
/* \
* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION \
* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_FRACTION \
* JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS \
* \
* Attempt to run a minor GC in the idle time if the free space falls below \
* this threshold or if it hasn't been collected for too long. The absolute \
* threshold is used when the nursery is large and the percentage when it is \
* small. See Nursery::shouldCollect(). \
*/ \
_(JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION, size_t, \
nurseryFreeThresholdForIdleCollection, ConvertSize, NoCheck, \
ChunkSize / 4) \
_(JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT, double, \
nurseryFreeThresholdForIdleCollectionFraction, ConvertTimes100, \
CheckNonZeroUnitRange, 0.25) \
_(JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS, mozilla::TimeDuration, \
nurseryTimeoutForIdleCollection, ConvertMillis, NoCheck, \
mozilla::TimeDuration::FromSeconds(5)) \
\
/* \
* JSGC_BALANCED_HEAP_LIMITS_ENABLED \
* JSGC_HEAP_GROWTH_FACTOR \
*/ \
_(JSGC_BALANCED_HEAP_LIMITS_ENABLED, bool, balancedHeapLimitsEnabled, \
ConvertBool, NoCheck, false) \
_(JSGC_HEAP_GROWTH_FACTOR, double, heapGrowthFactor, ConvertDouble, NoCheck, \
50.0) \
\
/* \
* 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. \
*/ \
_(JSGC_PRETENURE_THRESHOLD, double, pretenureThreshold, ConvertTimes100, \
CheckNonZeroUnitRange, 0.6) \
\
/* \
* JSGC_PRETENURE_STRING_THRESHOLD \
* \
* If the percentage of the tenured strings exceeds this threshold, string \
* will be allocated in tenured heap instead. (Default is allocated in \
* nursery.) \
*/ \
_(JSGC_PRETENURE_STRING_THRESHOLD, double, pretenureStringThreshold, \
ConvertTimes100, CheckNonZeroUnitRange, 0.55) \
\
/* \
* JSGC_STOP_PRETENURE_STRING_THRESHOLD \
* \
* If the finalization rate of the tenured strings exceeds this threshold, \
* string will be allocated in nursery. \
*/ \
_(JSGC_STOP_PRETENURE_STRING_THRESHOLD, double, \
stopPretenureStringThreshold, ConvertTimes100, CheckNonZeroUnitRange, 0.9) \
\
/* \
* JSGC_MIN_LAST_DITCH_GC_PERIOD \
* \
* Last ditch GC is skipped if allocation failure occurs less than this many \
* seconds from the previous one. \
*/ \
_(JSGC_MIN_LAST_DITCH_GC_PERIOD, mozilla::TimeDuration, \
minLastDitchGCPeriod, ConvertSeconds, NoCheck, \
TimeDuration::FromSeconds(60)) \
\
/* \
* JSGC_PARALLEL_MARKING_THRESHOLD_KB \
*/ \
_(JSGC_PARALLEL_MARKING_THRESHOLD_KB, size_t, parallelMarkingThresholdBytes, \
ConvertKB, NoCheck, 10 * 1024 * 1024)
namespace js {
class ZoneAllocator;
@ -539,6 +340,55 @@ struct Cell;
*/
namespace TuningDefaults {
/* JSGC_MAX_BYTES */
static const size_t GCMaxBytes = 0xffffffff;
/* JSGC_ALLOCATION_THRESHOLD */
static const size_t GCZoneAllocThresholdBase = 27 * 1024 * 1024;
/*
* JSGC_MIN_NURSERY_BYTES
*
* With some testing (Bug 1532838) we increased this to 256K from 192K
* which improves performance. We should try to reduce this for background
* tabs.
*/
static const size_t GCMinNurseryBytes = 256 * 1024;
/*
* JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
*
* This must be greater than 1.3 to maintain performance on splay-latency.
*/
static const double SmallHeapIncrementalLimit = 1.50;
/* JSGC_LARGE_HEAP_INCREMENTAL_LIMIT */
static const double LargeHeapIncrementalLimit = 1.10;
/* JSGC_ZONE_ALLOC_DELAY_KB */
static const size_t ZoneAllocDelayBytes = 1024 * 1024;
/* JSGC_HIGH_FREQUENCY_TIME_LIMIT */
static const auto HighFrequencyThreshold = 1; // in seconds
/* JSGC_SMALL_HEAP_SIZE_MAX */
static const size_t SmallHeapSizeMaxBytes = 100 * 1024 * 1024;
/* JSGC_LARGE_HEAP_SIZE_MIN */
static const size_t LargeHeapSizeMinBytes = 500 * 1024 * 1024;
/* JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH */
static const double HighFrequencySmallHeapGrowth = 3.0;
/* JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH */
static const double HighFrequencyLargeHeapGrowth = 1.5;
/* JSGC_LOW_FREQUENCY_HEAP_GROWTH */
static const double LowFrequencyHeapGrowth = 1.5;
/* JSGC_HEAP_GROWTH_FACTOR */
static const double HeapGrowthFactor = 50.0;
/* JSGC_MIN_EMPTY_CHUNK_COUNT */
static const uint32_t MinEmptyChunkCount = 1;
@ -560,15 +410,51 @@ static const bool CompactingEnabled = true;
/* JSGC_PARALLEL_MARKING_ENABLED */
static const bool ParallelMarkingEnabled = false;
/* JSGC_BALANCED_HEAP_LIMITS_ENABLED */
static const bool BalancedHeapLimitsEnabled = false;
/* JSGC_INCREMENTAL_WEAKMAP_ENABLED */
static const bool IncrementalWeakMapMarkingEnabled = true;
/* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION */
static const uint32_t NurseryFreeThresholdForIdleCollection = ChunkSize / 4;
/* JSGC_NURSERY_FREE_THRESHOLD_FOR_IDLE_COLLECTION_PERCENT */
static const double NurseryFreeThresholdForIdleCollectionFraction = 0.25;
/* JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS */
static const uint32_t NurseryTimeoutForIdleCollectionMS = 5000;
/* JSGC_PRETENURE_THRESHOLD */
static const double PretenureThreshold = 0.6;
/* JSGC_PRETENURE_GROUP_THRESHOLD */
static const double PretenureGroupThreshold = 3000;
/* JSGC_PRETENURE_STRING_THRESHOLD */
static const double PretenureStringThreshold = 0.55;
/* JSGC_STOP_PRETENURE_STRING_THRESHOLD */
static const double StopPretenureStringThreshold = 0.9;
/* JSGC_MIN_LAST_DITCH_GC_PERIOD */
static const auto MinLastDitchGCPeriod = 60; // in seconds
/* JSGC_MALLOC_THRESHOLD_BASE */
static const size_t MallocThresholdBase = 38 * 1024 * 1024;
/* JSGC_HELPER_THREAD_RATIO */
static const double HelperThreadRatio = 0.5;
/* JSGC_MAX_HELPER_THREADS */
static const size_t MaxHelperThreads = 8;
/* JSGC_URGENT_THRESHOLD_MB */
static const size_t UrgentThresholdBytes = 16 * 1024 * 1024;
/* JSGC_PARALLEL_MARKING_THRESHOLD_KB */
static const size_t ParallelMarkingThresholdBytes = 10 * 1024 * 1024;
} // namespace TuningDefaults
/*
@ -576,28 +462,242 @@ static const size_t MaxHelperThreads = 8;
* should only be modified by setParameter.
*/
class GCSchedulingTunables {
#define DEFINE_TUNABLE_FIELD(key, type, name, convert, check, default) \
MainThreadOrGCTaskData<type> name##_;
FOR_EACH_GC_TUNABLE(DEFINE_TUNABLE_FIELD)
#undef DEFINE_TUNABLE_FIELD
/*
* JSGC_MAX_BYTES
*
* Maximum nominal heap before last ditch GC.
*/
MainThreadData<size_t> gcMaxBytes_;
/*
* JSGC_MIN_NURSERY_BYTES
* JSGC_MAX_NURSERY_BYTES
*
* Minimum and maximum nursery size for each runtime.
*/
MainThreadData<size_t> gcMinNurseryBytes_;
MainThreadData<size_t> gcMaxNurseryBytes_;
/*
* JSGC_ALLOCATION_THRESHOLD
*
* The base value used to compute zone->threshold.bytes(). When
* gcHeapSize.bytes() exceeds threshold.bytes() for a zone, the zone may be
* scheduled for a GC, depending on the exact circumstances.
*/
MainThreadData<size_t> gcZoneAllocThresholdBase_;
/*
* JSGC_SMALL_HEAP_INCREMENTAL_LIMIT
*
* Multiple of threshold.bytes() which triggers a non-incremental GC.
*/
MainThreadData<double> smallHeapIncrementalLimit_;
/*
* JSGC_LARGE_HEAP_INCREMENTAL_LIMIT
*
* Multiple of threshold.bytes() which triggers a non-incremental GC.
*/
MainThreadData<double> largeHeapIncrementalLimit_;
/*
* 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.
*/
MainThreadData<size_t> zoneAllocDelayBytes_;
/*
* JSGC_HIGH_FREQUENCY_TIME_LIMIT
*
* We enter high-frequency mode if we GC a twice within this many
* microseconds.
*/
MainThreadData<mozilla::TimeDuration> highFrequencyThreshold_;
/*
* JSGC_SMALL_HEAP_SIZE_MAX
* JSGC_LARGE_HEAP_SIZE_MIN
* JSGC_HIGH_FREQUENCY_SMALL_HEAP_GROWTH
* JSGC_HIGH_FREQUENCY_LARGE_HEAP_GROWTH
*
* When in the |highFrequencyGC| mode, these parameterize the per-zone
* "HeapGrowthFactor" computation.
*/
MainThreadData<size_t> smallHeapSizeMaxBytes_;
MainThreadData<size_t> largeHeapSizeMinBytes_;
MainThreadData<double> highFrequencySmallHeapGrowth_;
MainThreadData<double> highFrequencyLargeHeapGrowth_;
/*
* JSGC_LOW_FREQUENCY_HEAP_GROWTH
*
* When not in |highFrequencyGC| mode, this is the global (stored per-zone)
* "HeapGrowthFactor".
*/
MainThreadData<double> lowFrequencyHeapGrowth_;
/*
* JSGC_BALANCED_HEAP_LIMITS_ENABLED
*/
MainThreadOrGCTaskData<bool> balancedHeapLimitsEnabled_;
/*
* JSGC_HEAP_GROWTH_FACTOR
*/
MainThreadOrGCTaskData<double> heapGrowthFactor_;
/*
* 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. The absolute threshold is used when the nursery is
* large and the percentage when it is small. See Nursery::shouldCollect()
*/
MainThreadData<uint32_t> nurseryFreeThresholdForIdleCollection_;
MainThreadData<double> nurseryFreeThresholdForIdleCollectionFraction_;
/* See JSGC_NURSERY_TIMEOUT_FOR_IDLE_COLLECTION_MS. */
MainThreadData<mozilla::TimeDuration> nurseryTimeoutForIdleCollection_;
/*
* 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.
*/
MainThreadData<double> pretenureThreshold_;
/*
* JSGC_PRETENURE_GROUP_THRESHOLD
*
* During a single nursery collection, if this many objects from the same
* object group are tenured, then that group will be pretenured.
*/
MainThreadData<uint32_t> pretenureGroupThreshold_;
/*
* JSGC_PRETENURE_STRING_THRESHOLD
*
* If the percentage of the tenured strings exceeds this threshold, string
* will be allocated in tenured heap instead. (Default is allocated in
* nursery.)
*/
MainThreadData<double> pretenureStringThreshold_;
/*
* JSGC_STOP_PRETENURE_STRING_THRESHOLD
*
* If the finalization rate of the tenured strings exceeds this threshold,
* string will be allocated in nursery.
*/
MainThreadData<double> stopPretenureStringThreshold_;
/*
* JSGC_MIN_LAST_DITCH_GC_PERIOD
*
* Last ditch GC is skipped if allocation failure occurs less than this many
* seconds from the previous one.
*/
MainThreadData<mozilla::TimeDuration> minLastDitchGCPeriod_;
/*
* JSGC_MALLOC_THRESHOLD_BASE
*
* The base value used to compute the GC trigger for malloc allocated memory.
*/
MainThreadOrGCTaskData<size_t> mallocThresholdBase_;
/*
* JSGC_URGENT_THRESHOLD_MB
*
* The base value used to compute the GC trigger for malloc allocated memory.
*/
MainThreadData<size_t> urgentThresholdBytes_;
/*
* JSGC_PARALLEL_MARKING_THRESHOLD_KB
*/
MainThreadData<size_t> parallelMarkingThresholdBytes_;
public:
GCSchedulingTunables();
#define DEFINE_TUNABLE_ACCESSOR(key, type, name, convert, check, default) \
type name() const { return name##_; }
FOR_EACH_GC_TUNABLE(DEFINE_TUNABLE_ACCESSOR)
#undef DEFINE_TUNABLE_ACCESSOR
size_t gcMaxBytes() const { return gcMaxBytes_; }
size_t gcMinNurseryBytes() const { return gcMinNurseryBytes_; }
size_t gcMaxNurseryBytes() const { return gcMaxNurseryBytes_; }
size_t gcZoneAllocThresholdBase() const { return gcZoneAllocThresholdBase_; }
double smallHeapIncrementalLimit() const {
return smallHeapIncrementalLimit_;
}
double largeHeapIncrementalLimit() const {
return largeHeapIncrementalLimit_;
}
size_t zoneAllocDelayBytes() const { return zoneAllocDelayBytes_; }
const mozilla::TimeDuration& highFrequencyThreshold() const {
return highFrequencyThreshold_;
}
size_t smallHeapSizeMaxBytes() const { return smallHeapSizeMaxBytes_; }
size_t largeHeapSizeMinBytes() const { return largeHeapSizeMinBytes_; }
double highFrequencySmallHeapGrowth() const {
return highFrequencySmallHeapGrowth_;
}
double highFrequencyLargeHeapGrowth() const {
return highFrequencyLargeHeapGrowth_;
}
double lowFrequencyHeapGrowth() const { return lowFrequencyHeapGrowth_; }
bool balancedHeapLimitsEnabled() const { return balancedHeapLimitsEnabled_; }
double heapGrowthFactor() const { return heapGrowthFactor_; }
uint32_t nurseryFreeThresholdForIdleCollection() const {
return nurseryFreeThresholdForIdleCollection_;
}
double nurseryFreeThresholdForIdleCollectionFraction() const {
return nurseryFreeThresholdForIdleCollectionFraction_;
}
mozilla::TimeDuration nurseryTimeoutForIdleCollection() const {
return nurseryTimeoutForIdleCollection_;
}
bool attemptPretenuring() const { return pretenureThreshold_ < 1.0; }
double pretenureThreshold() const { return pretenureThreshold_; }
uint32_t pretenureGroupThreshold() const { return pretenureGroupThreshold_; }
double pretenureStringThreshold() const { return pretenureStringThreshold_; }
double stopPretenureStringThreshold() const {
return stopPretenureStringThreshold_;
}
mozilla::TimeDuration minLastDitchGCPeriod() const {
return minLastDitchGCPeriod_;
}
size_t mallocThresholdBase() const { return mallocThresholdBase_; }
size_t urgentThresholdBytes() const { return urgentThresholdBytes_; }
size_t parallelMarkingThresholdBytes() const {
return parallelMarkingThresholdBytes_;
}
uint32_t getParameter(JSGCParamKey key);
[[nodiscard]] bool setParameter(JSGCParamKey key, uint32_t value);
void resetParameter(JSGCParamKey key);
private:
void maintainInvariantsAfterUpdate(JSGCParamKey updated);
void checkInvariants();
void setSmallHeapSizeMaxBytes(size_t value);
void setLargeHeapSizeMinBytes(size_t value);
void setHighFrequencySmallHeapGrowth(double value);
void setHighFrequencyLargeHeapGrowth(double value);
void setLowFrequencyHeapGrowth(double value);
void setHeapGrowthFactor(double value);
void setMinEmptyChunkCount(uint32_t value);
void setMaxEmptyChunkCount(uint32_t value);
static bool megabytesToBytes(uint32_t value, size_t* bytesOut);
static bool kilobytesToBytes(uint32_t value, size_t* bytesOut);
};
class GCSchedulingState {

View file

@ -9,44 +9,42 @@ load(libdir + "asserts.js");
const chunkSizeKB = gcparam('chunkBytes') / 1024;
const pageSizeKB = gcparam('systemPageSizeKB');
const testSizesKB = [128, 129, 255, 256, 516, 1023, 1024, 3*1024, 4*1024+1, 16*1024];
var testSizesKB = [128, 129, 255, 256, 516, 1023, 1024, 3*1024, 4*1024+1, 16*1024];
// Valid maximum sizes must be >= 1MB.
const testMaxSizesKB = testSizesKB.filter(x => x >= 1024);
var testMaxSizesKB = testSizesKB.filter(x => x >= 1024);
for (let max of testMaxSizesKB) {
for (var max of testMaxSizesKB) {
// Don't test minimums greater than the maximum.
for (let min of testSizesKB.filter(x => x <= max)) {
for (var min of testSizesKB.filter(x => x <= max)) {
setMinMax(min, max);
}
}
// The above loops raised the nursery size. Now reduce it to ensure that
// forcibly-reducing it works correctly.
gcparam('minNurseryBytes', 256*1024); // need to avoid min > max;
setMinMax(256, 1024);
// Try invalid configurations.
const badSizesKB = [ 0, 1, 129 * 1024];
function assertParamOutOfRange(f) {
assertErrorMessage(f, Object, "Parameter value out of range");
}
for (let size of badSizesKB) {
assertParamOutOfRange(() => gcparam('minNurseryBytes', size * 1024));
assertParamOutOfRange(() => gcparam('maxNurseryBytes', size * 1024));
}
// Try an invalid configuration.
assertErrorMessage(
() => setMinMax(2*1024, 1024),
Object,
"Parameter value out of range");
function setMinMax(min, max) {
gcparam('minNurseryBytes', min * 1024);
// Set the maximum first so that we don't hit a case where max < min.
gcparam('maxNurseryBytes', max * 1024);
assertEq(gcparam('minNurseryBytes'), nearestLegalSize(min) * 1024);
gcparam('minNurseryBytes', min * 1024);
assertEq(gcparam('maxNurseryBytes'), nearestLegalSize(max) * 1024);
assertEq(gcparam('minNurseryBytes'), nearestLegalSize(min) * 1024);
allocateSomeThings();
gc();
}
function allocateSomeThings() {
for (let i = 0; i < 1000; i++) {
let obj = { an: 'object', with: 'fields' };
for (var i = 0; i < 1000; i++) {
var obj = { an: 'object', with: 'fields' };
}
}

View file

@ -1,37 +1,40 @@
gczeal(0);
function testGetParam(key) {
gcparam(key);
gcparam(key);
}
function testChangeParam(key, diff) {
if (!diff) {
diff = 1;
}
let prev = gcparam(key);
function testChangeParam(key) {
let prev = gcparam(key);
let value = prev - 1;
try {
gcparam(key, value);
assertEq(gcparam(key), value);
gcparam(key, prev);
assertEq(gcparam(key), prev);
} catch {
assertEq(gcparam(key), prev);
}
}
let newValue = prev > 0 ? prev - diff : prev + diff;
gcparam(key, newValue);
assertEq(gcparam(key), newValue);
gcparam(key, prev);
assertEq(gcparam(key), prev);
function testMBParamValue(key) {
let prev = gcparam(key);
const value = 1024;
try {
gcparam(key, value);
assertEq(gcparam(key), value);
} catch {
assertEq(gcparam(key), prev);
}
gcparam(key, prev);
}
testGetParam("gcBytes");
testGetParam("gcNumber");
testGetParam("unusedChunks");
testGetParam("totalChunks");
testGetParam("nurseryBytes");
testGetParam("majorGCNumber");
testGetParam("minorGCNumber");
testGetParam("chunkBytes");
testGetParam("helperThreadCount");
testChangeParam("maxBytes");
testChangeParam("minNurseryBytes", 16 * 1024);
testChangeParam("maxNurseryBytes", 1024 * 1024);
testChangeParam("incrementalGCEnabled");
testChangeParam("perZoneGCEnabled");
testChangeParam("sliceTimeBudgetMS");
@ -49,16 +52,10 @@ testChangeParam("largeHeapIncrementalLimit");
testChangeParam("minEmptyChunkCount");
testChangeParam("maxEmptyChunkCount");
testChangeParam("compactingEnabled");
testChangeParam("parallelMarkingEnabled");
testChangeParam("parallelMarkingThresholdKB");
testChangeParam("minLastDitchGCPeriod");
testChangeParam("nurseryFreeThresholdForIdleCollection");
testChangeParam("nurseryFreeThresholdForIdleCollectionPercent");
testChangeParam("nurseryTimeoutForIdleCollectionMS");
testChangeParam("pretenureThreshold");
testChangeParam("zoneAllocDelayKB");
testChangeParam("mallocThresholdBase");
testChangeParam("urgentThreshold");
testChangeParam("nurseryTimeoutForIdleCollectionMS");
testChangeParam("helperThreadRatio");
testChangeParam("maxHelperThreads");
testChangeParam("parallelMarkingThresholdKB");
testMBParamValue("smallHeapSizeMax");
testMBParamValue("largeHeapSizeMin");