Created
June 5, 2019 17:22
-
-
Save hotsphink/8489d2eb39b434f857297e8c142736dc to your computer and use it in GitHub Desktop.
mkgist-created gist
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# HG changeset patch | |
# User Steve Fink <[email protected]> | |
# Date 1517297227 28800 | |
# Mon Jan 29 23:27:07 2018 -0800 | |
# Node ID 236b85186605693383e2a4999b87c1b5c4e9a338 | |
# Parent e8c553a7196328fed522cc5d4bafc78dcdc1a099 | |
# EXP-Topic pretenure.dedupe | |
Bug 903519 - M-dedupe | |
diff --git a/README.txt b/README.txt | |
--- a/README.txt | |
+++ b/README.txt | |
@@ -20,8 +20,9 @@ are accessible on Google Groups, or news | |
You can download nightly development builds from the Mozilla FTP server. | |
Keep in mind that nightly builds, which are used by Mozilla developers for | |
testing, may be buggy. Firefox nightlies, for example, can be found at: | |
https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/ | |
- or - | |
https://nightly.mozilla.org/ | |
+zoof | |
diff --git a/js/src/gc/GC.cpp b/js/src/gc/GC.cpp | |
--- a/js/src/gc/GC.cpp | |
+++ b/js/src/gc/GC.cpp | |
@@ -6688,17 +6688,23 @@ GCRuntime::finishCollection() | |
assertBackgroundSweepingFinished(); | |
MOZ_ASSERT(marker.isDrained()); | |
marker.stop(); | |
clearBufferedGrayRoots(); | |
uint64_t currentTime = PRMJ_Now(); | |
schedulingState.updateHighFrequencyMode(lastGCTime, currentTime, tunables); | |
+ bool nurseryStringsEnabled = rt->gc.nursery().canAllocateStrings(); | |
+ | |
for (ZonesIter zone(rt, WithAtoms); !zone.done(); zone.next()) { | |
+ // If we have disabled nursery strings due to too much tenuring, turn | |
+ // them back on in case the patterns have changed. | |
+ zone->setNurseryStringsEnabled(nurseryStringsEnabled); | |
+ | |
if (zone->isCollecting()) { | |
zone->changeGCState(Zone::Finished, Zone::NoGC); | |
zone->notifyObservingDebuggers(); | |
} | |
MOZ_ASSERT(!zone->isCollectingFromAnyThread()); | |
MOZ_ASSERT(!zone->wasGCStarted()); | |
} | |
diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp | |
--- a/js/src/gc/Marking.cpp | |
+++ b/js/src/gc/Marking.cpp | |
@@ -3203,25 +3203,54 @@ js::TenuringTracer::moveToTenured(JSStri | |
{ | |
MOZ_ASSERT(IsInsideNursery(src)); | |
MOZ_ASSERT(!src->zone()->usedByHelperThread()); | |
AllocKind dstKind = src->getAllocKind(); | |
Zone* zone = src->zone(); | |
zone->tenuredStrings++; | |
- TenuredCell* t = zone->arenas.allocateFromFreeList(dstKind, Arena::thingSize(dstKind)); | |
- if (!t) { | |
- AutoEnterOOMUnsafeRegion oomUnsafe; | |
- t = runtime()->gc.refillFreeListInGC(zone, dstKind); | |
- if (!t) | |
- oomUnsafe.crash(ChunkSize, "Failed to allocate string while tenuring."); | |
+ JSString* dst; | |
+ bool cacheable = src->length() < MaxCachedStringLength && src->isLinear() && src->asLinear().hasLatin1Chars(); | |
+ JS::AutoCheckCannotGC nogc; | |
+ if (cacheable && stringCache[src->length()] && memcmp(src->asLinear().latin1Chars(nogc), stringCache[src->length()]->asLinear().latin1Chars(nogc), src->length()) == 0) | |
+ { | |
+ dst = stringCache[src->length()]; | |
+ } else { | |
+ TenuredCell* t = zone->arenas.allocateFromFreeList(dstKind, Arena::thingSize(dstKind)); | |
+ if (!t) { | |
+ AutoEnterOOMUnsafeRegion oomUnsafe; | |
+ t = runtime()->gc.refillFreeListInGC(zone, dstKind); | |
+ if (!t) | |
+ oomUnsafe.crash(ChunkSize, "Failed to allocate string while tenuring."); | |
+ } | |
+ dst = reinterpret_cast<JSString*>(t); | |
+ tenuredSize += moveStringToTenured(dst, src, dstKind); | |
} | |
- JSString* dst = reinterpret_cast<JSString*>(t); | |
- tenuredSize += moveStringToTenured(dst, src, dstKind); | |
+ | |
+ if (cacheable) | |
+ stringCache[src->length()] = dst; | |
+ | |
+ if (false) { | |
+ size_t N = 40; | |
+ JS::AutoCheckCannotGC nogc; | |
+ if (dst->isLinear()) { | |
+ if (dst->asLinear().hasLatin1Chars()) { | |
+ const unsigned char* s = dst->asLinear().latin1Chars(nogc); | |
+ printf("%c %zd %.*s\n", dst->hasLatin1Chars() ? 'L' : 'D', dst->length(), | |
+ int(js::Min(N, dst->length())), s); | |
+ } else { | |
+ const wchar_t* s = reinterpret_cast<const wchar_t*>(dst->asLinear().twoByteChars(nogc)); | |
+ printf("%c %zd %.*ls\n", dst->hasLatin1Chars() ? 'L' : 'D', dst->length(), | |
+ int(js::Min(N, dst->length())), s); | |
+ } | |
+ } else { | |
+ printf("%c %zd\n", dst->hasLatin1Chars() ? 'L' : 'D', dst->length()); | |
+ } | |
+ } | |
RelocationOverlay* overlay = RelocationOverlay::fromCell(src); | |
overlay->forwardTo(dst); | |
insertIntoStringFixupList(overlay); | |
TracePromoteToTenured(src, dst); | |
return dst; | |
} | |
diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp | |
--- a/js/src/gc/Nursery.cpp | |
+++ b/js/src/gc/Nursery.cpp | |
@@ -543,16 +543,17 @@ js::TenuringTracer::TenuringTracer(JSRun | |
: JSTracer(rt, JSTracer::TracerKindTag::Tenuring, TraceWeakMapKeysValues) | |
, nursery_(*nursery) | |
, tenuredSize(0) | |
, objHead(nullptr) | |
, objTail(&objHead) | |
, stringHead(nullptr) | |
, stringTail(&stringHead) | |
{ | |
+ memset(stringCache, 0, sizeof(stringCache)); // FIXME: PodZero | |
} | |
inline float | |
js::Nursery::calcPromotionRate(bool *validForTenuring) const { | |
float used = float(previousGC.nurseryUsedBytes); | |
float capacity = float(previousGC.nurseryCapacity); | |
float tenured = float(previousGC.tenuredBytes); | |
float rate; | |
@@ -685,66 +686,72 @@ IsFullStoreBufferReason(JS::gcreason::Re | |
return reason == JS::gcreason::FULL_WHOLE_CELL_BUFFER || | |
reason == JS::gcreason::FULL_GENERIC_BUFFER || | |
reason == JS::gcreason::FULL_VALUE_BUFFER || | |
reason == JS::gcreason::FULL_CELL_PTR_BUFFER || | |
reason == JS::gcreason::FULL_SLOT_BUFFER || | |
reason == JS::gcreason::FULL_SHAPE_BUFFER; | |
} | |
-float | |
+void | |
js::Nursery::pretenure(JS::gcreason::Reason reason, | |
- const TenureCountCache& tenureCounts) | |
+ const TenureCountCache& tenureCounts, | |
+ float* promotionRate, | |
+ uint32_t* objectPretenureCount) | |
+ | |
{ | |
JSRuntime* rt = runtime(); | |
bool validPromotionRate; | |
- const float promotionRate = calcPromotionRate(&validPromotionRate); | |
+ *promotionRate = calcPromotionRate(&validPromotionRate); | |
+ *objectPretenureCount = 0; | |
+ | |
uint32_t pretenureCount = 0; | |
- bool shouldPretenure = (validPromotionRate && promotionRate > 0.6) || | |
+ bool shouldPretenure = (validPromotionRate && *promotionRate > 0.6) || | |
IsFullStoreBufferReason(reason); | |
- if (shouldPretenure) { | |
- JSContext* cx = TlsContext.get(); | |
- for (auto& entry : tenureCounts.entries) { | |
- if (entry.count >= int(OBJECT_PRETENURE_THRESHOLD)) { | |
- ObjectGroup* group = entry.group; | |
- if (group->canPreTenure() && group->zone()->group()->canEnterWithoutYielding(cx)) { | |
- AutoCompartment ac(cx, group); | |
- group->setShouldPreTenure(cx); | |
- pretenureCount++; | |
- } | |
+ // Clear out the tenuredString counts no matter what we decide. | |
+ auto atexit = mozilla::MakeScopeExit([&]() { | |
+ for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) | |
+ zone->tenuredStrings = 0; | |
+ }); | |
+ | |
+ if (!shouldPretenure) | |
+ return; | |
+ | |
+ JSContext* cx = TlsContext.get(); | |
+ for (auto& entry : tenureCounts.entries) { | |
+ if (entry.count >= int(OBJECT_PRETENURE_THRESHOLD)) { | |
+ ObjectGroup* group = entry.group; | |
+ if (group->canPreTenure() && group->zone()->group()->canEnterWithoutYielding(cx)) { | |
+ AutoCompartment ac(cx, group); | |
+ group->setShouldPreTenure(cx); | |
+ pretenureCount++; | |
} | |
} | |
} | |
+ *objectPretenureCount = pretenureCount; | |
+ | |
+ mozilla::Maybe<AutoTraceSession> session; | |
+ | |
for (ZonesIter zone(rt, SkipAtoms); !zone.done(); zone.next()) { | |
- fprintf(stderr, "shouldPretenure = %d alloc = %d tenured %d >= %d\n", | |
- shouldPretenure, (int) zone->allocNurseryStrings, (int) zone->tenuredStrings, STRING_ZONE_PRETENURE_THRESHOLD); | |
- if (shouldPretenure && zone->allocNurseryStrings && zone->tenuredStrings >= STRING_ZONE_PRETENURE_THRESHOLD) { | |
- JSRuntime::AutoProhibitActiveContextChange apacc(rt); | |
- CancelOffThreadIonCompile(zone); | |
- bool preserving = zone->isPreservingCode(); | |
- zone->setPreservingCode(false); | |
- zone->discardJitCode(rt->defaultFreeOp()); | |
- zone->setPreservingCode(preserving); | |
- for (CompartmentsInZoneIter c(zone); !c.done(); c.next()) { | |
- if (jit::JitCompartment* jitComp = c->jitCompartment()) { | |
- jitComp->discardStubs(); | |
- jitComp->stringsCanBeInNursery = false; | |
- } | |
- } | |
- zone->allocNurseryStrings = false; | |
- fprintf(stderr, "%p turn nursery strings off\n", (void*) zone); | |
+ fprintf(stderr, "alloc = %d tenured %d >= %d\n", | |
+ (int) zone->allocNurseryStrings, | |
+ (int) zone->tenuredStrings, STRING_ZONE_PRETENURE_THRESHOLD); | |
+ if (zone->allocNurseryStrings && zone->tenuredStrings >= STRING_ZONE_PRETENURE_THRESHOLD) { | |
+ if (session.isNothing()) | |
+ session.emplace(rt, JS::HeapState::MinorCollecting); | |
+ zone->setNurseryStringsEnabled(false); | |
} | |
zone->tenuredStrings = 0; | |
} | |
- rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount); | |
- | |
- return promotionRate; | |
+ // We were already iterating through the zones, so we did the zeroing along | |
+ // the way. | |
+ atexit.release(); | |
} | |
void | |
js::Nursery::collect(JS::gcreason::Reason reason) | |
{ | |
MOZ_ASSERT(!TlsContext.get()->suppressGC); | |
if (!isEnabled() || isEmpty()) { | |
@@ -792,18 +799,20 @@ js::Nursery::collect(JS::gcreason::Reaso | |
// Resize the nursery. | |
maybeResizeNursery(reason); | |
// If we are promoting the nursery, or exhausted the store buffer with | |
// pointers to nursery things, which will force a collection well before | |
// the nursery is full, look for object groups that are getting promoted | |
// excessively and try to pretenure them. | |
+ float promotionRate; | |
+ uint32_t pretenureCount; | |
startProfile(ProfileKey::Pretenure); | |
- float promotionRate = pretenure(reason, tenureCounts); | |
+ pretenure(reason, tenureCounts, &promotionRate, &pretenureCount); | |
endProfile(ProfileKey::Pretenure); | |
// We ignore gcMaxBytes when allocating for minor collection. However, if we | |
// overflowed, we disable the nursery. The next time we allocate, we'll fail | |
// because gcBytes >= gcMaxBytes. | |
if (rt->gc.usage.gcBytes() >= rt->gc.tunables.gcMaxBytes()) | |
@@ -818,16 +827,17 @@ js::Nursery::collect(JS::gcreason::Reaso | |
rt->gc.incMinorGcNumber(); | |
TimeDuration totalTime = profileDurations_[ProfileKey::Total]; | |
rt->addTelemetry(JS_TELEMETRY_GC_MINOR_US, totalTime.ToMicroseconds()); | |
rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON, reason); | |
if (totalTime.ToMilliseconds() > 1.0) | |
rt->addTelemetry(JS_TELEMETRY_GC_MINOR_REASON_LONG, reason); | |
rt->addTelemetry(JS_TELEMETRY_GC_NURSERY_BYTES, sizeOfHeapCommitted()); | |
+ rt->addTelemetry(JS_TELEMETRY_GC_PRETENURE_COUNT, pretenureCount); | |
rt->gc.stats().endNurseryCollection(reason); | |
TraceMinorGCEnd(); | |
timeInChunkAlloc_ = mozilla::TimeDuration(); | |
if (enableProfiling_ && totalTime >= profileThreshold_) { | |
rt->gc.stats().maybePrintProfileHeaders(); | |
diff --git a/js/src/gc/Nursery.h b/js/src/gc/Nursery.h | |
--- a/js/src/gc/Nursery.h | |
+++ b/js/src/gc/Nursery.h | |
@@ -82,16 +82,19 @@ class TenuringTracer : public JSTracer | |
// to find things held live by intra-Nursery pointers. | |
gc::RelocationOverlay* objHead; | |
gc::RelocationOverlay** objTail; | |
gc::RelocationOverlay* stringHead; | |
gc::RelocationOverlay** stringTail; | |
TenuringTracer(JSRuntime* rt, Nursery* nursery); | |
+ static constexpr size_t MaxCachedStringLength = 10; | |
+ JSString* stringCache[MaxCachedStringLength]; | |
+ | |
public: | |
Nursery& nursery() { return nursery_; } | |
template <typename T> void traverse(T** thingp); | |
template <typename T> void traverse(T* thingp); | |
// The store buffers need to be able to call these directly. | |
void traceObject(JSObject* src); | |
@@ -248,18 +251,20 @@ class Nursery | |
/* Do a minor collection. */ | |
void collect(JS::gcreason::Reason reason); | |
/* | |
* At the end of a collection, decide which object groups should be | |
* pretenured, as well as what zones should disable nursery strings. | |
* Returns the promotion rate as a fraction. | |
*/ | |
- float pretenure(JS::gcreason::Reason reason, | |
- const js::gc::TenureCountCache& tenureCounts); | |
+ void pretenure(JS::gcreason::Reason reason, | |
+ const js::gc::TenureCountCache& tenureCounts, | |
+ float* promotionRate, | |
+ uint32_t* objectPretenureCount); | |
/* | |
* If the thing at |*ref| in the Nursery has been forwarded, set |*ref| to | |
* the new location and return true. Otherwise return false and leave | |
* |*ref| unset. | |
*/ | |
MOZ_ALWAYS_INLINE MOZ_MUST_USE static bool getForwardedPointer(js::gc::Cell** ref); | |
diff --git a/js/src/gc/Zone.cpp b/js/src/gc/Zone.cpp | |
--- a/js/src/gc/Zone.cpp | |
+++ b/js/src/gc/Zone.cpp | |
@@ -395,16 +395,39 @@ Zone::deleteEmptyCompartment(JSCompartme | |
compartments().erase(&i); | |
comp->destroy(runtimeFromActiveCooperatingThread()->defaultFreeOp()); | |
return; | |
} | |
} | |
MOZ_CRASH("Compartment not found"); | |
} | |
+void | |
+Zone::setNurseryStringsEnabled(bool enable) | |
+{ | |
+ if (allocNurseryStrings == enable) | |
+ return; | |
+ | |
+ JSRuntime* rt = runtimeFromActiveCooperatingThread(); | |
+ JSRuntime::AutoProhibitActiveContextChange apacc(rt); | |
+ CancelOffThreadIonCompile(this); | |
+ bool preserving = isPreservingCode(); | |
+ setPreservingCode(false); | |
+ discardJitCode(rt->defaultFreeOp()); | |
+ setPreservingCode(preserving); | |
+ for (CompartmentsInZoneIter c(this); !c.done(); c.next()) { | |
+ if (jit::JitCompartment* jitComp = c->jitCompartment()) { | |
+ jitComp->discardStubs(); | |
+ jitComp->stringsCanBeInNursery = enable; | |
+ } | |
+ } | |
+ allocNurseryStrings = enable; | |
+ fprintf(stderr, "%p turn nursery strings %s\n", (void*) this, enable ? "on" : "off"); | |
+} | |
+ | |
ZoneList::ZoneList() | |
: head(nullptr), tail(nullptr) | |
{} | |
ZoneList::ZoneList(Zone* zone) | |
: head(zone), tail(zone) | |
{ | |
MOZ_RELEASE_ASSERT(!zone->isOnList()); | |
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 | |
@@ -649,16 +649,21 @@ struct Zone : public JS::shadow::Zone, | |
} | |
void setKeepShapeTables(bool b) { | |
keepShapeTables_ = b; | |
} | |
// Delete an empty compartment after its contents have been merged. | |
void deleteEmptyCompartment(JSCompartment* comp); | |
+ // Control whether strings can be allocated in the nursery for this zone, | |
+ // invalidating all jitcode as necessary. | |
+ void | |
+ setNurseryStringsEnabled(bool enable); | |
+ | |
/* | |
* This variation of calloc will call the large-allocation-failure callback | |
* on OOM and retry the allocation. | |
*/ | |
template <typename T> | |
T* pod_callocCanGC(size_t numElems) { | |
T* p = pod_calloc<T>(numElems); | |
if (MOZ_LIKELY(!!p)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment