1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-03-23 07:12:12 -07:00

Maintain a moving average of the mortality of each generation.

Copied from Perforce
 Change: 191081
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Gareth Rees 2016-04-13 15:19:50 +01:00
parent 473a6f2a82
commit 3174eba71e
15 changed files with 304 additions and 184 deletions

View file

@ -470,7 +470,14 @@
#define VM_ARENA_SIZE_DEFAULT ((Size)1 << 28)
/* Stack configuration -- see <code/sp*.c> */
/* Locus configuration -- see <code/locus.c> */
/* Weighting for the current observation, in the exponential moving
* average computation of the mortality of a generation. */
#define LocusMortalityALPHA (0.4)
/* Stack probe configuration -- see <code/sp*.c> */
/* Currently StackProbe has a useful implementation only on Windows. */
#if defined(MPS_OS_W3)

View file

@ -37,7 +37,7 @@
#define EVENT_VERSION_MAJOR ((unsigned)1)
#define EVENT_VERSION_MEDIAN ((unsigned)6)
#define EVENT_VERSION_MINOR ((unsigned)0)
#define EVENT_VERSION_MINOR ((unsigned)1)
/* EVENT_LIST -- list of event types and general properties
@ -67,7 +67,7 @@
*/
#define EventNameMAX ((size_t)19)
#define EventCodeMAX ((EventCode)0x0087)
#define EventCodeMAX ((EventCode)0x0088)
#define EVENT_LIST(EVENT, X) \
/* 0123456789012345678 <- don't exceed without changing EventNameMAX */ \
@ -189,11 +189,12 @@
EVENT(X, AMCTraceEnd , 0x0081, TRUE, Trace) \
EVENT(X, TraceCreatePoolGen , 0x0082, TRUE, Trace) \
/* new events for performance analysis of large heaps. */ \
EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) \
/* EVENT(X, TraceCondemnZones , 0x0083, TRUE, Trace) */ \
EVENT(X, ArenaGenZoneAdd , 0x0084, TRUE, Arena) \
EVENT(X, ArenaUseFreeZone , 0x0085, TRUE, Arena) \
/* EVENT(X, ArenaBlacklistZone , 0x0086, TRUE, Arena) */ \
EVENT(X, PauseTimeSet , 0x0087, TRUE, Arena)
EVENT(X, PauseTimeSet , 0x0087, TRUE, Arena) \
EVENT(X, TraceEndGen , 0x0088, TRUE, Trace)
/* Remember to update EventNameMAX and EventCodeMAX above!
@ -722,11 +723,6 @@
PARAM(X, 9, W, newDeferredSize) /* new size (deferred) of pool gen */ \
PARAM(X, 10, W, oldDeferredSize) /* old size (deferred) of pool gen */
#define EVENT_TraceCondemnZones_PARAMS(PARAM, X) \
PARAM(X, 0, P, trace) /* the trace */ \
PARAM(X, 1, W, condemnedSet) /* the condemned zoneSet */ \
PARAM(X, 2, W, white) /* the trace's white zoneSet */
#define EVENT_ArenaGenZoneAdd_PARAMS(PARAM, X) \
PARAM(X, 0, P, arena) /* the arena */ \
PARAM(X, 1, P, gendesc) /* the generation description */ \
@ -740,6 +736,14 @@
PARAM(X, 0, P, arena) /* the arena */ \
PARAM(X, 1, D, pauseTime) /* the new maximum pause time, in seconds */
#define EVENT_TraceEndGen_PARAMS(PARAM, X) \
PARAM(X, 0, P, trace) /* the trace */ \
PARAM(X, 1, P, gen) /* the generation */ \
PARAM(X, 2, W, condemned) /* bytes condemned in generation */ \
PARAM(X, 3, W, forwarded) /* bytes forwarded from generation */ \
PARAM(X, 4, W, preservedInPlace) /* bytes preserved in generation */ \
PARAM(X, 5, D, mortality) /* updated mortality */
#endif /* eventdef_h */

View file

@ -123,8 +123,8 @@ static Bool GenParamCheck(GenParamStruct *params)
{
CHECKL(params != NULL);
CHECKL(params->capacity > 0);
CHECKL(params->mortality > 0.0);
CHECKL(params->mortality < 1.0);
CHECKL(params->mortality >= 0.0);
CHECKL(params->mortality <= 1.0);
return TRUE;
}
@ -174,6 +174,79 @@ Size GenDescNewSize(GenDesc gen)
}
/* genDescTraceStart -- notify generation of start of a trace */
static void genDescStartTrace(GenDesc gen, Trace trace)
{
GenTraceStats stats;
AVERT(GenDesc, gen);
AVERT(Trace, trace);
stats = &gen->trace[trace->ti];
stats->condemned = 0;
stats->forwarded = 0;
stats->preservedInPlace = 0;
}
/* genDescEndTrace -- notify generation of end of a trace */
static void genDescEndTrace(GenDesc gen, Trace trace)
{
GenTraceStats stats;
Size survived;
AVERT(GenDesc, gen);
AVERT(Trace, trace);
stats = &gen->trace[trace->ti];
survived = stats->forwarded + stats->preservedInPlace;
AVER(survived <= stats->condemned);
if (stats->condemned > 0) {
double mortality = 1.0 - survived / (double)stats->condemned;
double alpha = LocusMortalityALPHA;
gen->mortality = gen->mortality * (1 - alpha) + mortality * alpha;
EVENT6(TraceEndGen, trace, gen, stats->condemned, stats->forwarded,
stats->preservedInPlace, gen->mortality);
}
}
/* GenDescCondemned -- memory in a generation was condemned for a trace */
void GenDescCondemned(GenDesc gen, Trace trace, Size size)
{
GenTraceStats stats;
AVERT(GenDesc, gen);
AVERT(Trace, trace);
stats = &gen->trace[trace->ti];
stats->condemned += size;
trace->condemned += size;
}
/* GenDescSurvived -- memory in a generation survived a trace */
void GenDescSurvived(GenDesc gen, Trace trace, Size forwarded,
Size preservedInPlace)
{
GenTraceStats stats;
AVERT(GenDesc, gen);
AVERT(Trace, trace);
stats = &gen->trace[trace->ti];
stats->forwarded += forwarded;
stats->preservedInPlace += preservedInPlace;
trace->forwardedSize += forwarded;
trace->preservedInPlaceSize += preservedInPlace;
}
/* GenDescTotalSize -- return total size of generation */
Size GenDescTotalSize(GenDesc gen)
@ -196,6 +269,7 @@ Size GenDescTotalSize(GenDesc gen)
Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth)
{
Index i;
Res res;
Ring node, nextNode;
@ -213,6 +287,18 @@ Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth)
if (res != ResOK)
return res;
for (i = 0; i < NELEMS(gen->trace); ++i) {
GenTraceStats stats = &gen->trace[i];
res = WriteF(stream, depth + 2,
"trace $W {\n", (WriteFW)i,
" condemned $W\n", (WriteFW)stats->condemned,
" forwarded $W\n", (WriteFW)stats->forwarded,
" preservedInPlace $W\n", (WriteFW)stats->preservedInPlace,
"}\n", NULL);
if (res != ResOK)
return res;
}
RING_FOR(node, &gen->locusRing, nextNode) {
PoolGen pgen = RING_ELT(PoolGen, genRing, node);
res = PoolGenDescribe(pgen, stream, depth + 2);
@ -376,25 +462,35 @@ double ChainDeferral(Chain chain)
}
/* ChainStartGC -- called to notify start of GC for this chain */
/* ChainStartTrace -- called to notify start of GC for this chain */
void ChainStartGC(Chain chain, Trace trace)
void ChainStartTrace(Chain chain, Trace trace)
{
Index i;
AVERT(Chain, chain);
AVERT(Trace, trace);
chain->activeTraces = TraceSetAdd(chain->activeTraces, trace);
for (i = 0; i < chain->genCount; ++i)
genDescStartTrace(&chain->gens[i], trace);
}
/* ChainEndGC -- called to notify end of GC for this chain */
/* ChainEndTrace -- called to notify end of GC for this chain */
void ChainEndGC(Chain chain, Trace trace)
void ChainEndTrace(Chain chain, Trace trace)
{
Index i;
AVERT(Chain, chain);
AVERT(Trace, trace);
chain->activeTraces = TraceSetDel(chain->activeTraces, trace);
for (i = 0; i < chain->genCount; ++i)
genDescEndTrace(&chain->gens[i], trace);
}
@ -617,9 +713,10 @@ void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred)
/* PoolGenAccountForAge -- accounting for condemning
*
* Call this when memory is condemned via PoolWhiten. The size
* parameter should be the amount of memory that is being condemned
* for the first time. The deferred flag is as for PoolGenAccountForFill.
* Call this when memory is condemned via PoolWhiten, or when
* artificially ageing memory in PoolGenFree. The size parameter
* should be the amount of memory that is being condemned for the
* first time. The deferred flag is as for PoolGenAccountForFill.
*
* See <design/strategy/#accounting.op.age>
*/
@ -627,7 +724,8 @@ void PoolGenAccountForEmpty(PoolGen pgen, Size unused, Bool deferred)
void PoolGenAccountForAge(PoolGen pgen, Size size, Bool deferred)
{
AVERT(PoolGen, pgen);
AVERT(Bool, deferred);
if (deferred) {
AVER(pgen->newDeferredSize >= size);
pgen->newDeferredSize -= size;

View file

@ -17,11 +17,22 @@
typedef struct GenParamStruct *GenParam;
typedef struct GenParamStruct {
Size capacity; /* capacity in kB */
double mortality;
Size capacity; /* capacity in kB */
double mortality; /* predicted mortality */
} GenParamStruct;
/* GenTraceStats -- per-generation per-trace statistics */
typedef struct GenTraceStatsStruct *GenTraceStats;
typedef struct GenTraceStatsStruct {
Size condemned; /* size of objects condemned by the trace */
Size forwarded; /* size of objects that were forwarded by the trace */
Size preservedInPlace; /* size of objects preserved in place by the trace */
} GenTraceStatsStruct;
/* GenDesc -- descriptor of a generation in a chain */
typedef struct GenDescStruct *GenDesc;
@ -30,11 +41,12 @@ typedef struct GenDescStruct *GenDesc;
typedef struct GenDescStruct {
Sig sig;
ZoneSet zones; /* zoneset for this generation */
Size capacity; /* capacity in kB */
double mortality;
ZoneSet zones; /* zoneset for this generation */
Size capacity; /* capacity in kB */
double mortality; /* predicted mortality */
RingStruct locusRing; /* Ring of all PoolGen's in this GenDesc (locus) */
RingStruct segRing; /* Ring of GCSegs in this generation */
GenTraceStatsStruct trace[TraceLIMIT];
} GenDescStruct;
@ -79,6 +91,8 @@ typedef struct mps_chain_s {
extern Bool GenDescCheck(GenDesc gen);
extern Size GenDescNewSize(GenDesc gen);
extern Size GenDescTotalSize(GenDesc gen);
extern void GenDescCondemned(GenDesc gen, Trace trace, Size size);
extern void GenDescSurvived(GenDesc gen, Trace trace, Size forwarded, Size preservedInPlace);
extern Res GenDescDescribe(GenDesc gen, mps_lib_FILE *stream, Count depth);
extern Res ChainCreate(Chain *chainReturn, Arena arena, size_t genCount,
@ -87,8 +101,8 @@ extern void ChainDestroy(Chain chain);
extern Bool ChainCheck(Chain chain);
extern double ChainDeferral(Chain chain);
extern void ChainStartGC(Chain chain, Trace trace);
extern void ChainEndGC(Chain chain, Trace trace);
extern void ChainStartTrace(Chain chain, Trace trace);
extern void ChainEndTrace(Chain chain, Trace trace);
extern size_t ChainGens(Chain chain);
extern GenDesc ChainGen(Chain chain, Index gen);
extern Res ChainDescribe(Chain chain, mps_lib_FILE *stream, Count depth);

View file

@ -440,9 +440,7 @@ typedef struct ScanStateStruct {
STATISTIC_DECL(Count nailCount); /* segments nailed by ambig refs */
STATISTIC_DECL(Count snapCount); /* refs snapped to forwarded objs */
STATISTIC_DECL(Count forwardedCount); /* objects preserved by moving */
Size forwardedSize; /* bytes preserved by moving */
STATISTIC_DECL(Count preservedInPlaceCount); /* objects preserved in place */
Size preservedInPlaceSize; /* bytes preserved in place */
STATISTIC_DECL(Size copiedSize); /* bytes copied */
STATISTIC_DECL(Size scannedSize); /* bytes scanned */
} ScanStateStruct;

View file

@ -326,13 +326,13 @@ Bool PolicyStartTrace(Trace *traceReturn, Arena arena)
res = TraceCreate(&trace, arena, TraceStartWhyCHAIN_GEN0CAP);
AVER(res == ResOK);
trace->chain = firstChain;
ChainStartTrace(firstChain, trace);
res = policyCondemnChain(&mortality, firstChain, trace);
if (res != ResOK) /* should try some other trace, really @@@@ */
goto failCondemn;
if (TraceIsEmpty(trace))
goto nothingCondemned;
trace->chain = firstChain;
ChainStartGC(firstChain, trace);
res = TraceStart(trace, mortality, trace->condemned * TraceWorkFactor);
/* We don't expect normal GC traces to fail to start. */
AVER(res == ResOK);

View file

@ -93,6 +93,7 @@ typedef struct amcSegStruct {
GCSegStruct gcSegStruct; /* superclass fields must come first */
amcGen gen; /* generation this segment belongs to */
Nailboard board; /* nailboard for this segment or NULL if none */
Size forwarded[TraceLIMIT]; /* size of objects forwarded for each trace */
BOOLFIELD(old); /* .seg.old */
BOOLFIELD(deferred); /* .seg.deferred */
Sig sig; /* <code/misc.h#sig> */
@ -1207,10 +1208,6 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg)
}
}
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
condemned += SegSize(seg);
trace->condemned += condemned;
amc = PoolAMC(pool);
AVERT(AMC, amc);
@ -1221,6 +1218,10 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg)
amcseg->old = TRUE;
}
amcseg->forwarded[trace->ti] = 0;
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
GenDescCondemned(gen->pgen.gen, trace, condemned + SegSize(seg));
/* Ensure we are forwarding into the right generation. */
/* see <design/poolamc/#gen.ramp> */
@ -1635,7 +1636,6 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO)
length = AddrOffset(ref, clientQ); /* .exposed.seg */
STATISTIC_STAT(++ss->forwardedCount);
ss->forwardedSize += length;
do {
res = BUFFER_RESERVE(&newBase, buffer, length);
if (res != ResOK)
@ -1695,6 +1695,7 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg)
Count preservedInPlaceCount = (Count)0;
Size preservedInPlaceSize = (Size)0;
AMC amc;
PoolGen pgen;
Size headerSize;
Addr padBase; /* base of next padding object */
Size padLength; /* length of next padding object */
@ -1773,19 +1774,19 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg)
AVER(bytesReclaimed <= SegSize(seg));
trace->reclaimSize += bytesReclaimed;
trace->preservedInPlaceCount += preservedInPlaceCount;
trace->preservedInPlaceSize += preservedInPlaceSize;
pgen = &amcSegGen(seg)->pgen;
GenDescSurvived(pgen->gen, trace, Seg2amcSeg(seg)->forwarded[trace->ti],
preservedInPlaceSize);
/* Free the seg if we can; fixes .nailboard.limitations.middle. */
if(preservedInPlaceCount == 0
&& (SegBuffer(seg) == NULL)
&& (SegNailed(seg) == TraceSetEMPTY)) {
amcGen gen = amcSegGen(seg);
/* We may not free a buffered seg. */
AVER(SegBuffer(seg) == NULL);
PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred);
PoolGenFree(pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred);
}
}
@ -1832,6 +1833,7 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg)
trace->reclaimSize += SegSize(seg);
GenDescSurvived(gen->pgen.gen, trace, Seg2amcSeg(seg)->forwarded[trace->ti], 0);
PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, Seg2amcSeg(seg)->deferred);
}

View file

@ -1078,7 +1078,7 @@ static Res AMSWhiten(Pool pool, Trace trace, Seg seg)
AMS ams;
AMSSeg amsseg;
Buffer buffer; /* the seg's buffer, if it has one */
Count uncondemned;
Count uncondemnedGrains, condemnedGrains;
AVERT(Pool, pool);
ams = PoolAMS(pool);
@ -1128,21 +1128,23 @@ static Res AMSWhiten(Pool pool, Trace trace, Seg seg)
AMS_RANGE_BLACKEN(seg, scanLimitIndex, limitIndex);
amsRangeWhiten(seg, limitIndex, amsseg->grains);
/* We didn't condemn the buffer, subtract it from the count. */
uncondemned = limitIndex - scanLimitIndex;
uncondemnedGrains = limitIndex - scanLimitIndex;
} else { /* condemn whole seg */
amsRangeWhiten(seg, 0, amsseg->grains);
uncondemned = (Count)0;
uncondemnedGrains = (Count)0;
}
/* The unused part of the buffer remains new: the rest becomes old. */
PoolGenAccountForAge(&ams->pgen, AMSGrainsSize(ams, amsseg->newGrains - uncondemned), FALSE);
amsseg->oldGrains += amsseg->newGrains - uncondemned;
amsseg->newGrains = uncondemned;
condemnedGrains = amsseg->newGrains - uncondemnedGrains;
PoolGenAccountForAge(&ams->pgen, AMSGrainsSize(ams, condemnedGrains), FALSE);
amsseg->oldGrains += condemnedGrains;
amsseg->newGrains = uncondemnedGrains;
amsseg->marksChanged = FALSE; /* <design/poolams/#marked.condemn> */
amsseg->ambiguousFixes = FALSE;
if (amsseg->oldGrains > 0) {
trace->condemned += AMSGrainsSize(ams, amsseg->oldGrains);
GenDescCondemned(ams->pgen.gen, trace,
AMSGrainsSize(ams, amsseg->oldGrains));
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
} else {
amsseg->colourTablesInUse = FALSE;
@ -1550,6 +1552,7 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg)
AMS ams;
AMSSeg amsseg;
Count nowFree, grains, reclaimedGrains;
Size preservedInPlaceSize;
PoolDebugMixin debug;
AVERT(Pool, pool);
@ -1601,7 +1604,8 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg)
PoolGenAccountForReclaim(&ams->pgen, AMSGrainsSize(ams, reclaimedGrains), FALSE);
trace->reclaimSize += AMSGrainsSize(ams, reclaimedGrains);
/* preservedInPlaceCount is updated on fix */
trace->preservedInPlaceSize += AMSGrainsSize(ams, amsseg->oldGrains);
preservedInPlaceSize = AMSGrainsSize(ams, amsseg->oldGrains);
GenDescSurvived(ams->pgen.gen, trace, 0, preservedInPlaceSize);
/* Ensure consistency of segment even if are just about to free it */
amsseg->colourTablesInUse = FALSE;

View file

@ -753,7 +753,7 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg)
AWL awl;
AWLSeg awlseg;
Buffer buffer;
Count uncondemned;
Count uncondemnedGrains, condemnedGrains;
/* All parameters checked by generic PoolWhiten. */
@ -769,13 +769,13 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg)
if(buffer == NULL) {
awlRangeWhiten(awlseg, 0, awlseg->grains);
uncondemned = (Count)0;
uncondemnedGrains = (Count)0;
} else {
/* Whiten everything except the buffer. */
Addr base = SegBase(seg);
Index scanLimitIndex = awlIndexOfAddr(base, awl, BufferScanLimit(buffer));
Index limitIndex = awlIndexOfAddr(base, awl, BufferLimit(buffer));
uncondemned = limitIndex - scanLimitIndex;
uncondemnedGrains = limitIndex - scanLimitIndex;
awlRangeWhiten(awlseg, 0, scanLimitIndex);
awlRangeWhiten(awlseg, limitIndex, awlseg->grains);
@ -788,12 +788,14 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg)
}
}
PoolGenAccountForAge(&awl->pgen, AWLGrainsSize(awl, awlseg->newGrains - uncondemned), FALSE);
awlseg->oldGrains += awlseg->newGrains - uncondemned;
awlseg->newGrains = uncondemned;
condemnedGrains = awlseg->newGrains - uncondemnedGrains;
PoolGenAccountForAge(&awl->pgen, AWLGrainsSize(awl, condemnedGrains), FALSE);
awlseg->oldGrains += condemnedGrains;
awlseg->newGrains = uncondemnedGrains;
if (awlseg->oldGrains > 0) {
trace->condemned += AWLGrainsSize(awl, awlseg->oldGrains);
GenDescCondemned(awl->pgen.gen, trace,
AWLGrainsSize(awl, awlseg->oldGrains));
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
}
@ -1172,7 +1174,7 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg)
trace->reclaimSize += AWLGrainsSize(awl, reclaimedGrains);
trace->preservedInPlaceCount += preservedInPlaceCount;
trace->preservedInPlaceSize += preservedInPlaceSize;
GenDescSurvived(awl->pgen.gen, trace, 0, preservedInPlaceSize);
SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace));
if (awlseg->freeGrains == awlseg->grains && buffer == NULL)

View file

@ -381,8 +381,7 @@ static void loSegReclaim(LOSeg loseg, Trace trace)
trace->reclaimSize += LOGrainsSize(lo, reclaimedGrains);
trace->preservedInPlaceCount += preservedInPlaceCount;
trace->preservedInPlaceSize += preservedInPlaceSize;
GenDescSurvived(lo->pgen.gen, trace, 0, preservedInPlaceSize);
SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace));
if (!marked)
@ -671,7 +670,7 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg)
LO lo;
LOSeg loseg;
Buffer buffer;
Count grains, uncondemned;
Count grains, uncondemnedGrains, condemnedGrains;
AVERT(Pool, pool);
lo = PoolPoolLO(pool);
@ -691,21 +690,25 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg)
Addr base = SegBase(seg);
Index scanLimitIndex = loIndexOfAddr(base, lo, BufferScanLimit(buffer));
Index limitIndex = loIndexOfAddr(base, lo, BufferLimit(buffer));
uncondemned = limitIndex - scanLimitIndex;
uncondemnedGrains = limitIndex - scanLimitIndex;
if (0 < scanLimitIndex)
BTCopyInvertRange(loseg->alloc, loseg->mark, 0, scanLimitIndex);
if (limitIndex < grains)
BTCopyInvertRange(loseg->alloc, loseg->mark, limitIndex, grains);
} else {
uncondemned = (Count)0;
uncondemnedGrains = (Count)0;
BTCopyInvertRange(loseg->alloc, loseg->mark, 0, grains);
}
PoolGenAccountForAge(&lo->pgen, LOGrainsSize(lo, loseg->newGrains - uncondemned), FALSE);
loseg->oldGrains += loseg->newGrains - uncondemned;
loseg->newGrains = uncondemned;
trace->condemned += LOGrainsSize(lo, loseg->oldGrains);
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
condemnedGrains = loseg->newGrains - uncondemnedGrains;
PoolGenAccountForAge(&lo->pgen, LOGrainsSize(lo, condemnedGrains), FALSE);
loseg->oldGrains += condemnedGrains;
loseg->newGrains = uncondemnedGrains;
if (loseg->oldGrains > 0) {
GenDescCondemned(lo->pgen.gen, trace, LOGrainsSize(lo, loseg->oldGrains));
SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace));
}
return ResOK;
}

View file

@ -105,9 +105,7 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena,
STATISTIC(ss->nailCount = (Count)0);
STATISTIC(ss->snapCount = (Count)0);
STATISTIC(ss->forwardedCount = (Count)0);
ss->forwardedSize = (Size)0; /* see .message.data */
STATISTIC(ss->preservedInPlaceCount = (Count)0);
ss->preservedInPlaceSize = (Size)0; /* see .message.data */
STATISTIC(ss->copiedSize = (Size)0);
ss->scannedSize = (Size)0; /* see .work */
ss->sig = ScanStateSig;
@ -296,11 +294,7 @@ static void traceUpdateCounts(Trace trace, ScanState ss,
STATISTIC(trace->nailCount += ss->nailCount);
STATISTIC(trace->snapCount += ss->snapCount);
STATISTIC(trace->forwardedCount += ss->forwardedCount);
trace->forwardedSize += ss->forwardedSize; /* see .message.data */
STATISTIC(trace->preservedInPlaceCount += ss->preservedInPlaceCount);
trace->preservedInPlaceSize += ss->preservedInPlaceSize;
return;
}
@ -741,6 +735,33 @@ found:
}
/* traceDestroyCommon -- common functionality for TraceDestroy* */
static void traceDestroyCommon(Trace trace)
{
Ring chainNode, nextChainNode;
if (trace->chain != NULL) {
ChainEndTrace(trace->chain, trace);
} else {
/* Notify all the chains. */
RING_FOR(chainNode, &trace->arena->chainRing, nextChainNode) {
Chain chain = RING_ELT(Chain, chainRing, chainNode);
ChainEndTrace(chain, trace);
}
}
EVENT1(TraceDestroy, trace);
trace->sig = SigInvalid;
trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace);
trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace);
/* Hopefully the trace reclaimed some memory, so clear any emergency. */
ArenaSetEmergency(trace->arena, FALSE);
}
/* TraceDestroyInit -- destroy a trace object in state INIT */
void TraceDestroyInit(Trace trace)
@ -748,14 +769,9 @@ void TraceDestroyInit(Trace trace)
AVERT(Trace, trace);
AVER(trace->state == TraceINIT);
AVER(trace->condemned == 0);
AVER(!TraceSetIsMember(trace->arena->flippedTraces, trace));
EVENT1(TraceDestroy, trace);
trace->sig = SigInvalid;
trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace);
/* Clear the emergency flag so the next trace starts normally. */
ArenaSetEmergency(trace->arena, FALSE);
traceDestroyCommon(trace);
}
@ -773,19 +789,6 @@ void TraceDestroyFinished(Trace trace)
AVERT(Trace, trace);
AVER(trace->state == TraceFINISHED);
if(trace->chain == NULL) {
Ring chainNode, nextChainNode;
/* Notify all the chains. */
RING_FOR(chainNode, &trace->arena->chainRing, nextChainNode) {
Chain chain = RING_ELT(Chain, chainRing, chainNode);
ChainEndGC(chain, trace);
}
} else {
ChainEndGC(trace->chain, trace);
}
STATISTIC_STAT(EVENT13
(TraceStatScan, trace,
trace->rootScanCount, trace->rootScanSize,
@ -808,14 +811,7 @@ void TraceDestroyFinished(Trace trace)
(TraceStatReclaim, trace,
trace->reclaimCount, trace->reclaimSize));
EVENT1(TraceDestroy, trace);
trace->sig = SigInvalid;
trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace);
trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace);
/* Hopefully the trace reclaimed some memory, so clear any emergency. */
ArenaSetEmergency(trace->arena, FALSE);
traceDestroyCommon(trace);
}
@ -1490,7 +1486,7 @@ static Res traceCondemnAll(Trace trace)
{
Res res;
Arena arena;
Ring poolNode, nextPoolNode, chainNode, nextChainNode;
Ring poolNode, nextPoolNode;
arena = trace->arena;
AVERT(Arena, arena);
@ -1522,12 +1518,6 @@ static Res traceCondemnAll(Trace trace)
if (TraceIsEmpty(trace))
return ResFAIL;
/* Notify all the chains. */
RING_FOR(chainNode, &arena->chainRing, nextChainNode) {
Chain chain = RING_ELT(Chain, chainRing, chainNode);
ChainStartGC(chain, trace);
}
return ResOK;
failBegin:
@ -1749,12 +1739,20 @@ Res TraceStartCollectAll(Trace *traceReturn, Arena arena, int why)
Trace trace = NULL;
Res res;
double finishingTime;
Ring chainNode, nextChainNode;
AVERT(Arena, arena);
AVER(arena->busyTraces == TraceSetEMPTY);
res = TraceCreate(&trace, arena, why);
AVER(res == ResOK); /* succeeds because no other trace is busy */
/* Notify all the chains. */
RING_FOR(chainNode, &arena->chainRing, nextChainNode) {
Chain chain = RING_ELT(Chain, chainRing, chainNode);
ChainStartTrace(chain, trace);
}
res = traceCondemnAll(trace);
if(res != ResOK) /* should try some other trace, really @@@@ */
goto failCondemn;

View file

@ -34,45 +34,32 @@ short-lived objects.)
First, the effect of varying the capacity of a chain with a single
generation.
======== ========= =========================
Capacity Mortality Execution time (user+sys)
======== ========= =========================
100 0.80 362.6
200 0.80 354.9
400 0.80 349.7
800 0.80 314.4
1600 0.80 215.7
3200 0.80 94.0
6400 0.80 53.5
12800 0.80 79.6
25600 0.80 77.6
======== ========= =========================
======== =========================
Capacity Execution time (user+sys)
======== =========================
100 362.6
200 354.9
400 349.7
800 314.4
1600 215.7
3200 94.0
6400 53.5
12800 79.6
25600 77.6
======== =========================
Second, the effect of varying the mortality of a chain with a single
generation.
======== ========= =========================
Capacity Mortality Execution time (user+sys)
======== ========= =========================
6400 0.20 55.4
6400 0.40 54.0
6400 0.60 54.0
6400 0.80 53.5
6400 0.99 54.8
======== ========= =========================
Third, the effect of varying the number of generations (all
Second, the effect of varying the number of generations (all
generations being identical).
=========== ======== ========= =========================
Generations Capacity Mortality Execution time (user+sys)
=========== ======== ========= =========================
1 6400 0.80 53.5
2 6400 0.80 42.4
3 6400 0.80 42.1
4 6400 0.80 42.2
5 6400 0.80 42.2
=========== ======== ========= =========================
=========== ======== =========================
Generations Capacity Execution time (user+sys)
=========== ======== =========================
1 6400 53.5
2 6400 42.4
3 6400 42.1
4 6400 42.2
5 6400 42.2
=========== ======== =========================
These tables suggest that:
@ -80,10 +67,6 @@ These tables suggest that:
sizes right is dramatic: much bigger than the small improvements to
gained from other techniques.
#. The predicted mortality doesn't make much difference to the overall
execution time (it does affect the distribution of pause times,
however: see :ref:`topic-collection-schedule`.)
#. You can make generations too big as well as too small.
#. There are rapidly diminishing returns to be gained from adding
@ -97,7 +80,7 @@ These tables suggest that:
The table below shows the effect of varying the initial allocation of
address space to the arena (using three generations each with capacity
6400 kB, mortality 0.80).
6400 kB).
============= ========== =========== =========================
Address space Extensions Collections Execution time (user+sys)

View file

@ -4,6 +4,22 @@ Release notes
=============
.. _release-notes-1.116:
Release 1.116.0
---------------
New features
............
#. The MPS now measures the mortality of a :term:`generation` each
time it is collected, and maintains a moving average. This means
that it is no longer important to provide an accurate estimate of
the mortality when creating a :term:`generation chain` by calling
:c:func:`mps_chain_create`..
.. _release-notes-1.115:
Release 1.115.0

View file

@ -461,8 +461,8 @@ Arena properties
When the pause time is short, the MPS needs to take more slices of
time in order to make :term:`garbage collection` progress, and
make more use of :term:`barriers (1)` to support
:term:`incremental collection`. This increases time overheads,
and especially operating system overheads.
:term:`incremental garbage collection`. This increases time
overheads, and especially operating system overheads.
The pause time may be set to zero, in which case the MPS returns
as soon as it can, without regard for overall efficiency. This
@ -476,7 +476,7 @@ Arena properties
The pause time may be set to infinity, in which case the MPS
completes all outstanding :term:`garbage collection` work before
returning from an operation. The consequence is that the MPS will
be able to save on the overheads due to :term:`incremental
be able to save on the overheads due to :term:`incremental garbage
collection`, leading to lower total time spent in collection. This
value is suitable for non-interactive applications where total
time is important.

View file

@ -55,8 +55,9 @@ you can choose which chain it should use by passing the
Create a generation chain by preparing an array of
:c:type:`mps_gen_param_s` structures giving the *capacity* (in
kilobytes) and *predicted mortality* (between 0 and 1) of each
generation, and passing them to :c:func:`mps_chain_create`.
kilobytes) and *initial predicted mortality* (between 0 and 1
inclusive) of each generation, and passing them to
:c:func:`mps_chain_create`.
When the *new size* of a generation exceeds its capacity, the MPS will
be prepared to start collecting the chain to which the generation
@ -95,17 +96,29 @@ For example::
} mps_gen_param_s;
``mps_capacity`` is the capacity of the generation, in
:term:`kilobytes`. When the size of the generation
exceeds this, the MPS will be prepared to start collecting it.
:term:`kilobytes`. When the size of the generation exceeds this,
the MPS will be prepared to start collecting it.
``mps_mortality`` is the predicted mortality of the generation:
the proportion (between 0 and 1) of blocks in the generation that
are expected to be :term:`dead` when the generation is collected.
.. note::
These numbers are hints to the MPS that it may use to make
decisions about when and what to collect: nothing will go wrong
(other than suboptimal performance) if you make poor
choices. See :ref:`topic-collection-schedule`.
The name *capacity* is somewhat misleading. When a generation
reaches its capacity the MPS may not be able to collect it
immediately (for example because some other generation is
being collected), but this does not prevent allocation into
the generation, and so the size of a generation will often
exceed its capacity.
``mps_mortality`` is the initial predicted mortality of the
generation: the proportion (between 0 and 1 inclusive) of bytes in
the generation that are expected to be :term:`dead` when the
generation is collected.
.. note::
This value is only used as an initial estimate. The MPS
measures the mortality each time it collects the generation,
and maintains a moving average. So it is not important to
provide an accurate estimate here.
.. c:function:: mps_res_t mps_chain_create(mps_chain_t *chain_o, mps_arena_t arena, size_t gen_count, mps_gen_param_s *gen_params)
@ -187,28 +200,6 @@ survive collection get promoted to generation *g*\+1. If the last
generation in the chain is collected, the survivors are promoted into
an :term:`arena`\-wide "top" generation.
The predicted mortality is used to estimate how long the collection
will take, and this is used in turn to decide how much work the
collector will do each time it has an opportunity to do some work. The constraints here are:
#. The :term:`client program` might have specified a limit on the
acceptable length of the pause if the work is being done inside
:c:func:`mps_arena_step`.
#. The collector needs to keep up with the :term:`client program`:
that is, it has to collect garbage at least as fast as the client
is producing it, otherwise the amount of garbage will grow without
bound.
With perfect prediction, the collector's work should be smoothly
distributed, with a small maximum pause time. Getting the predicted
mortality wrong leads to "lumpy" distribution of collection work with
a longer maximum pause time. If the predicted mortality is too high,
the collector will start out by taking small time slices and then find
that it has to catch up later by taking larger time slices. If the
predicted mortality is too low, the collector will take larger time
slices up front and then find that it is idle later on.
.. index::
single: garbage collection; start message