mirror of
git://git.sv.gnu.org/emacs.git
synced 2026-03-26 08:41:47 -07:00
Merge branch/2016-04-13/mortality.
Copied from Perforce Change: 192195 ServerID: perforce.ravenbrook.com
This commit is contained in:
commit
6764d08810
14 changed files with 296 additions and 175 deletions
|
|
@ -482,7 +482,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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
120
mps/code/locus.c
120
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 <design/strategy/#accounting.op.age>
|
||||
*/
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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 <design/poolamc/#gen.ramp> */
|
||||
|
|
@ -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;
|
||||
|
||||
/* <design/trace/#fix.noaver> */
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 <code/global.c#emergency.invariant>. */
|
||||
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 <code/global.c#emergency.invariant>. */
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
.................
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue