diff --git a/mps/code/config.h b/mps/code/config.h index 7e3d2ac5461..5e20c33c4c3 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -482,7 +482,14 @@ #define VM_ARENA_SIZE_DEFAULT ((Size)1 << 28) -/* Stack configuration -- see */ +/* Locus configuration -- see */ + +/* 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 */ /* Currently StackProbe has a useful implementation only on Windows. */ #if defined(MPS_OS_W3) diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index 6c3c12f8522..8797a750e26 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -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! @@ -713,11 +714,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 */ \ @@ -731,6 +727,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 */ diff --git a/mps/code/locus.c b/mps/code/locus.c index 82c3aa03cfc..a93fb72f0ae 100644 --- a/mps/code/locus.c +++ b/mps/code/locus.c @@ -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); } @@ -606,10 +702,10 @@ void PoolGenAccountForEmpty(PoolGen pgen, Size used, Size unused, Bool deferred) /* PoolGenAccountForAge -- accounting for condemning * - * Call this when memory is condemned via PoolWhiten. The parameters - * specify the amount of memory that was buffered/new and is now being - * condemned for the first time. The deferred flag is as for - * PoolGenAccountForEmpty. + * 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 PoolGenAccountForEmpty. * * See */ @@ -788,11 +884,9 @@ void LocusInit(Arena arena) /* Can't check arena, because it's not been inited. */ - /* TODO: The mortality estimate here is unjustifiable. Dynamic generation - decision making needs to be improved and this constant removed. */ gen->zones = ZoneSetEMPTY; gen->capacity = 0; /* unused */ - gen->mortality = 0.51; + gen->mortality = 0.5; RingInit(&gen->locusRing); RingInit(&gen->segRing); gen->sig = GenDescSig; diff --git a/mps/code/locus.h b/mps/code/locus.h index be4fa3bc875..91374491d2f 100644 --- a/mps/code/locus.h +++ b/mps/code/locus.h @@ -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; @@ -80,6 +92,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, @@ -88,8 +102,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); diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 2b246950b6f..04c19f63e92 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -430,9 +430,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 */ Size scannedSize; /* bytes scanned */ } ScanStateStruct; diff --git a/mps/code/policy.c b/mps/code/policy.c index bbead43efd6..77176853e5f 100644 --- a/mps/code/policy.c +++ b/mps/code/policy.c @@ -288,14 +288,15 @@ Bool PolicyStartTrace(Trace *traceReturn, Bool *collectWorldReturn, Res res; Trace trace; + AVER(traceReturn != NULL); + AVERT(Arena, arena); + if (collectWorldAllowed) { Size sFoundation, sCondemned, sSurvivors, sConsTrace; double tTracePerScan; /* tTrace/cScan */ double dynamicDeferral; /* Compute dynamic criterion. See strategy.lisp-machine. */ - AVER(arena->topGen.mortality >= 0.0); - AVER(arena->topGen.mortality <= 1.0); sFoundation = (Size)0; /* condemning everything, only roots @@@@ */ /* @@@@ sCondemned should be scannable only */ sCondemned = ArenaCommitted(arena) - ArenaSpareCommitted(arena); @@ -339,13 +340,13 @@ Bool PolicyStartTrace(Trace *traceReturn, Bool *collectWorldReturn, 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); diff --git a/mps/code/poolamc.c b/mps/code/poolamc.c index 20cc59cc8e4..e880141c5ed 100644 --- a/mps/code/poolamc.c +++ b/mps/code/poolamc.c @@ -102,6 +102,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(accountedAsBuffered); /* .seg.accounted-as-buffered */ BOOLFIELD(old); /* .seg.old */ BOOLFIELD(deferred); /* .seg.deferred */ @@ -980,11 +981,14 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, Arena arena; Seg seg; amcSeg amcseg; + TraceId ti; + Trace trace; AVERT(Buffer, buffer); AVER(BufferIsReady(buffer)); seg = BufferSeg(buffer); AVERT(Seg, seg); + amcseg = MustBeA(amcSeg, seg); AVER(init <= limit); arena = BufferArena(buffer); @@ -1002,12 +1006,16 @@ static void AMCBufferEmpty(Pool pool, Buffer buffer, ShieldExpose(arena, seg); (*pool->format->pad)(init, size); ShieldCover(arena, seg); + + /* The padding object is white, so needs to be accounted as condemned. */ + TRACE_SET_ITER(ti, trace, seg->white, arena) + GenDescCondemned(amcseg->gen->pgen.gen, trace, size); + TRACE_SET_ITER_END(ti, trace, seg->white, arena); } - amcseg = MustBeA(amcSeg, seg); if (amcseg->accountedAsBuffered) { /* Account the entire buffer (including the padding object) as used. */ - PoolGenAccountForEmpty(&amcSegGen(seg)->pgen, SegSize(seg), 0, + PoolGenAccountForEmpty(&amcseg->gen->pgen, SegSize(seg), 0, amcseg->deferred); amcseg->accountedAsBuffered = FALSE; } @@ -1162,10 +1170,6 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) } } - SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); - condemned += SegSize(seg); - trace->condemned += condemned; - gen = amcSegGen(seg); AVERT(amcGen, gen); if (!amcseg->old) { @@ -1179,6 +1183,10 @@ static Res AMCWhiten(Pool pool, Trace trace, Seg seg) PoolGenAccountForAge(&gen->pgen, 0, SegSize(seg), amcseg->deferred); } + 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 */ @@ -1501,6 +1509,8 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) amcGen gen; /* generation of old copy of object */ TraceSet grey; /* greyness of object being relocated */ Seg toSeg; /* segment to which object is being relocated */ + TraceId ti; + Trace trace; /* */ AVERT_CRITICAL(Pool, pool); @@ -1588,7 +1598,6 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) length = AddrOffset(ref, clientQ); /* .exposed.seg */ STATISTIC(++ss->forwardedCount); - ss->forwardedSize += length; do { res = BUFFER_RESERVE(&newBase, buffer, length); if (res != ResOK) @@ -1614,7 +1623,11 @@ static Res AMCFix(Pool pool, ScanState ss, Seg seg, Ref *refIO) ShieldCover(arena, toSeg); } while (!BUFFER_COMMIT(buffer, newBase, length)); + STATISTIC(ss->copiedSize += length); + TRACE_SET_ITER(ti, trace, ss->traces, ss->arena) + MustBeA(amcSeg, seg)->forwarded[ti] += length; + TRACE_SET_ITER_END(ti, trace, ss->traces, ss->arena); (*format->move)(ref, newRef); /* .exposed.seg */ @@ -1648,6 +1661,7 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) Count preservedInPlaceCount = (Count)0; Size preservedInPlaceSize = (Size)0; AMC amc = MustBeA(AMCZPool, pool); + PoolGen pgen; Size headerSize; Addr padBase; /* base of next padding object */ Size padLength; /* length of next padding object */ @@ -1720,19 +1734,19 @@ static void amcReclaimNailed(Pool pool, Trace trace, Seg seg) STATISTIC(AVER(bytesReclaimed <= SegSize(seg))); STATISTIC(trace->reclaimSize += bytesReclaimed); STATISTIC(trace->preservedInPlaceCount += preservedInPlaceCount); - trace->preservedInPlaceSize += preservedInPlaceSize; + pgen = &amcSegGen(seg)->pgen; + GenDescSurvived(pgen->gen, trace, MustBeA(amcSeg, seg)->forwarded[trace->ti], + preservedInPlaceSize); /* Free the seg if we can; fixes .nailboard.limitations.middle. */ if(preservedInPlaceCount == 0 && (!SegHasBuffer(seg)) && (SegNailed(seg) == TraceSetEMPTY)) { - amcGen gen = amcSegGen(seg); - /* We may not free a buffered seg. */ AVER(!SegHasBuffer(seg)); - PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, MustBeA(amcSeg, seg)->deferred); + PoolGenFree(pgen, seg, 0, SegSize(seg), 0, MustBeA(amcSeg, seg)->deferred); } } @@ -1745,10 +1759,11 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg) { AMC amc = MustBeA_CRITICAL(AMCZPool, pool); amcGen gen; + amcSeg amcseg; AVERT_CRITICAL(Trace, trace); AVERT_CRITICAL(Seg, seg); - + amcseg = MustBeA_CRITICAL(amcSeg, seg); gen = amcSegGen(seg); AVERT_CRITICAL(amcGen, gen); @@ -1776,7 +1791,8 @@ static void AMCReclaim(Pool pool, Trace trace, Seg seg) STATISTIC(trace->reclaimSize += SegSize(seg)); - PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, MustBeA(amcSeg, seg)->deferred); + GenDescSurvived(gen->pgen.gen, trace, amcseg->forwarded[trace->ti], 0); + PoolGenFree(&gen->pgen, seg, 0, SegSize(seg), 0, amcseg->deferred); } diff --git a/mps/code/poolams.c b/mps/code/poolams.c index 199921f9894..e1ae41749cc 100644 --- a/mps/code/poolams.c +++ b/mps/code/poolams.c @@ -1135,7 +1135,8 @@ static Res AMSWhiten(Pool pool, Trace trace, Seg seg) 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; @@ -1543,6 +1544,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); @@ -1594,7 +1596,8 @@ static void AMSReclaim(Pool pool, Trace trace, Seg seg) PoolGenAccountForReclaim(ams->pgen, AMSGrainsSize(ams, reclaimedGrains), FALSE); STATISTIC(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; diff --git a/mps/code/poolawl.c b/mps/code/poolawl.c index 5e4e4facb2e..4f0392018cf 100644 --- a/mps/code/poolawl.c +++ b/mps/code/poolawl.c @@ -758,7 +758,8 @@ static Res AWLWhiten(Pool pool, Trace trace, Seg seg) awlseg->newGrains = 0; 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)); } @@ -1087,7 +1088,7 @@ static void AWLReclaim(Pool pool, Trace trace, Seg seg) STATISTIC(trace->reclaimSize += AWLGrainsSize(awl, reclaimedGrains)); STATISTIC(trace->preservedInPlaceCount += preservedInPlaceCount); - trace->preservedInPlaceSize += preservedInPlaceSize; + GenDescSurvived(awl->pgen->gen, trace, 0, preservedInPlaceSize); SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); if (awlseg->freeGrains == awlseg->grains && !hasBuffer) { diff --git a/mps/code/poollo.c b/mps/code/poollo.c index defdd374643..e2c6d5834cf 100644 --- a/mps/code/poollo.c +++ b/mps/code/poollo.c @@ -360,8 +360,7 @@ static void loSegReclaim(LOSeg loseg, Trace trace) STATISTIC(trace->reclaimSize += LOGrainsSize(lo, reclaimedGrains)); STATISTIC(trace->preservedInPlaceCount += preservedInPlaceCount); - trace->preservedInPlaceSize += preservedInPlaceSize; - + GenDescSurvived(lo->pgen->gen, trace, 0, preservedInPlaceSize); SegSetWhite(seg, TraceSetDel(SegWhite(seg), trace)); if (!marked) { @@ -676,7 +675,6 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg) BTCopyInvertRange(loseg->alloc, loseg->mark, 0, grains); } - /* The unused part of the buffer remains buffered: the rest becomes old. */ AVER(loseg->bufferedGrains >= uncondemnedGrains); agedGrains = loseg->bufferedGrains - uncondemnedGrains; @@ -686,8 +684,10 @@ static Res LOWhiten(Pool pool, Trace trace, Seg seg) loseg->bufferedGrains = uncondemnedGrains; loseg->newGrains = 0; - trace->condemned += LOGrainsSize(lo, loseg->oldGrains); - SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + if (loseg->oldGrains > 0) { + GenDescCondemned(lo->pgen->gen, trace, LOGrainsSize(lo, loseg->oldGrains)); + SegSetWhite(seg, TraceSetAdd(SegWhite(seg), trace)); + } return ResOK; } diff --git a/mps/code/trace.c b/mps/code/trace.c index f4dd7dd317a..f171604049c 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -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; } @@ -742,13 +736,21 @@ found: } -/* TraceDestroyInit -- destroy a trace object in state INIT */ +/* traceDestroyCommon -- common functionality for TraceDestroy* */ -void TraceDestroyInit(Trace trace) +static void traceDestroyCommon(Trace trace) { - AVERT(Trace, trace); - AVER(trace->state == TraceINIT); - AVER(trace->condemned == 0); + 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); + } + } /* Ensure that address space is returned to the operating system for * traces that don't have any condemned objects (there might be @@ -757,11 +759,27 @@ void TraceDestroyInit(Trace trace) EVENT1(TraceDestroy, trace); + /* Hopefully the trace reclaimed some memory, so clear any emergency. + * Do this before removing the trace from busyTraces, to avoid + * violating . */ + ArenaSetEmergency(trace->arena, FALSE); + trace->sig = SigInvalid; trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace); + trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace); +} - /* Clear the emergency flag so the next trace starts normally. */ - ArenaSetEmergency(trace->arena, FALSE); + +/* TraceDestroyInit -- destroy a trace object in state INIT */ + +void TraceDestroyInit(Trace trace) +{ + AVERT(Trace, trace); + AVER(trace->state == TraceINIT); + AVER(trace->condemned == 0); + AVER(!TraceSetIsMember(trace->arena->flippedTraces, trace)); + + traceDestroyCommon(trace); } @@ -779,19 +797,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(EVENT13(TraceStatScan, trace, trace->rootScanCount, trace->rootScanSize, trace->rootCopiedSize, @@ -811,16 +816,7 @@ void TraceDestroyFinished(Trace trace) STATISTIC(EVENT3(TraceStatReclaim, trace, trace->reclaimCount, trace->reclaimSize)); - EVENT1(TraceDestroy, trace); - - /* Hopefully the trace reclaimed some memory, so clear any emergency. - * Do this before removing the trace from busyTraces, to avoid - * violating . */ - ArenaSetEmergency(trace->arena, FALSE); - - trace->sig = SigInvalid; - trace->arena->busyTraces = TraceSetDel(trace->arena->busyTraces, trace); - trace->arena->flippedTraces = TraceSetDel(trace->arena->flippedTraces, trace); + traceDestroyCommon(trace); } @@ -1493,7 +1489,7 @@ static Res traceCondemnAll(Trace trace) { Res res; Arena arena; - Ring poolNode, nextPoolNode, chainNode, nextChainNode; + Ring poolNode, nextPoolNode; arena = trace->arena; AVERT(Arena, arena); @@ -1525,12 +1521,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: @@ -1747,12 +1737,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; diff --git a/mps/manual/source/guide/perf.rst b/mps/manual/source/guide/perf.rst index 805d706f169..adea19cc761 100644 --- a/mps/manual/source/guide/perf.rst +++ b/mps/manual/source/guide/perf.rst @@ -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) diff --git a/mps/manual/source/release.rst b/mps/manual/source/release.rst index 4569a8b49f1..20125b9e0ba 100644 --- a/mps/manual/source/release.rst +++ b/mps/manual/source/release.rst @@ -3,11 +3,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`.. + + Interface changes ................. diff --git a/mps/manual/source/topic/collection.rst b/mps/manual/source/topic/collection.rst index d911172f413..e0c7563b471 100644 --- a/mps/manual/source/topic/collection.rst +++ b/mps/manual/source/topic/collection.rst @@ -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