1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-03-26 16:51:46 -07:00

Merging branch/2015-08-25/tradeoff to master sources.

Copied from Perforce
 Change: 190039
 ServerID: perforce.ravenbrook.com
This commit is contained in:
Richard Brooksby 2016-03-15 04:42:30 +00:00
commit 314193d8c2
16 changed files with 241 additions and 157 deletions

View file

@ -210,6 +210,8 @@ static void test(mps_arena_class_t arena_class, mps_arg_s arena_args[],
mps_class_mvt(), args), "stress MVT");
} MPS_ARGS_END(args);
/* Manual allocation should not cause any garbage collections. */
Insist(mps_collections(arena) == 0);
mps_arena_destroy(arena);
}

View file

@ -1312,6 +1312,7 @@ Size ArenaAvail(Arena arena)
this information from the operating system. It also depends on the
arena class, of course. */
AVER(sSwap >= arena->committed);
return sSwap - arena->committed + arena->spareCommitted;
}
@ -1321,7 +1322,20 @@ Size ArenaAvail(Arena arena)
Size ArenaCollectable(Arena arena)
{
/* Conservative estimate -- see job003929. */
return ArenaCommitted(arena) - ArenaSpareCommitted(arena);
Size committed = ArenaCommitted(arena);
Size spareCommitted = ArenaSpareCommitted(arena);
AVER(committed >= spareCommitted);
return committed - spareCommitted;
}
/* ArenaAccumulateTime -- accumulate time spent tracing */
void ArenaAccumulateTime(Arena arena, Clock start, Clock end)
{
AVERT(Arena, arena);
AVER(start <= end);
arena->tracedTime += (end - start) / (double) ClocksPerSec();
}

View file

@ -420,8 +420,9 @@
#define ARENA_MINIMUM_COLLECTABLE_SIZE ((Size)1000000)
/* ARENA_DEFAULT_COLLECTION_RATE is an estimate of the MPS's
* collection rate (in bytes per second), for use in the case where
* there isn't enough data to use a measured value. */
* collection rate (in work per second; see <design/type/#work>), for
* use in the case where there isn't enough data to use a measured
* value. */
#define ARENA_DEFAULT_COLLECTION_RATE (25000000.0)

View file

@ -36,7 +36,7 @@
*/
#define EVENT_VERSION_MAJOR ((unsigned)1)
#define EVENT_VERSION_MEDIAN ((unsigned)4)
#define EVENT_VERSION_MEDIAN ((unsigned)5)
#define EVENT_VERSION_MINOR ((unsigned)0)
@ -447,7 +447,7 @@
PARAM(X, 1, W, condemned) \
PARAM(X, 2, W, notCondemned) \
PARAM(X, 3, W, foundation) \
PARAM(X, 4, W, rate) \
PARAM(X, 4, W, quantumWork) \
PARAM(X, 5, D, mortality) \
PARAM(X, 6, D, finishingTime)
@ -655,7 +655,7 @@
#define EVENT_ArenaPoll_PARAMS(PARAM, X) \
PARAM(X, 0, P, arena) \
PARAM(X, 1, W, start) \
PARAM(X, 2, W, quanta)
PARAM(X, 2, B, workWasDone)
#define EVENT_ArenaSetEmergency_PARAMS(PARAM, X) \
PARAM(X, 0, P, arena) \
@ -674,7 +674,7 @@
PARAM(X, 4, W, notCondemned) /* collectible but not condemned bytes */ \
PARAM(X, 5, W, foundation) /* foundation size */ \
PARAM(X, 6, W, white) /* white reference set */ \
PARAM(X, 7, W, rate) /* segs to scan per increment */
PARAM(X, 7, W, quantumWork) /* tracing work to be done in each poll */
#define EVENT_VMCompact_PARAMS(PARAM, X) \
PARAM(X, 0, W, vmem0) /* pre-collection reserved size */ \

View file

@ -190,7 +190,7 @@ Bool GlobalsCheck(Globals arenaGlobals)
CHECKD_NOSIG(Ring, &arena->greyRing[rank]);
CHECKD_NOSIG(Ring, &arena->chainRing);
CHECKL(arena->tracedSize >= 0.0);
CHECKL(arena->tracedWork >= 0.0);
CHECKL(arena->tracedTime >= 0.0);
/* no check for arena->lastWorldCollect (Clock) */
@ -215,6 +215,8 @@ Bool GlobalsCheck(Globals arenaGlobals)
CHECKL(RingCheck(&arenaRing));
CHECKL(BoolCheck(arena->emergency));
/* There can only be an emergency when a trace is busy. */
CHECKL(!arena->emergency || arena->busyTraces != TraceSetEMPTY);
if (arenaGlobals->defaultChain != NULL)
CHECKD(Chain, arenaGlobals->defaultChain);
@ -289,7 +291,7 @@ Res GlobalsInit(Globals arenaGlobals)
arena->finalPool = NULL;
arena->busyTraces = TraceSetEMPTY; /* <code/trace.c> */
arena->flippedTraces = TraceSetEMPTY; /* <code/trace.c> */
arena->tracedSize = 0.0;
arena->tracedWork = 0.0;
arena->tracedTime = 0.0;
arena->lastWorldCollect = ClockNow();
arena->insideShield = FALSE; /* <code/shield.c> */
@ -708,8 +710,8 @@ void (ArenaPoll)(Globals globals)
{
Arena arena;
Clock start;
Count quanta;
Size tracedSize;
Bool moreWork, workWasDone = FALSE;
Work tracedWork;
AVERT(Globals, globals);
@ -725,35 +727,35 @@ void (ArenaPoll)(Globals globals)
/* fillMutatorSize has advanced; call TracePoll enough to catch up. */
start = ClockNow();
quanta = 0;
EVENT3(ArenaPoll, arena, start, 0);
EVENT3(ArenaPoll, arena, start, FALSE);
do {
tracedSize = TracePoll(globals);
if (tracedSize > 0) {
quanta += 1;
arena->tracedSize += tracedSize;
moreWork = TracePoll(&tracedWork, globals);
if (moreWork) {
workWasDone = TRUE;
}
} while (PolicyPollAgain(arena, start, tracedSize));
} while (PolicyPollAgain(arena, moreWork, tracedWork));
/* Don't count time spent checking for work, if there was no work to do. */
if(quanta > 0) {
arena->tracedTime += (ClockNow() - start) / (double) ClocksPerSec();
if (workWasDone) {
ArenaAccumulateTime(arena, start, ClockNow());
}
AVER(!PolicyPoll(arena));
EVENT3(ArenaPoll, arena, start, quanta);
EVENT3(ArenaPoll, arena, start, BOOLOF(workWasDone));
globals->insidePoll = FALSE;
}
/* ArenaStep -- use idle time for collection work */
Bool ArenaStep(Globals globals, double interval, double multiplier)
{
Size scanned;
Bool stepped;
Clock start, end, now;
Bool workWasDone = FALSE;
Clock start, intervalEnd, availableEnd, now;
Clock clocks_per_sec;
Arena arena;
@ -764,39 +766,45 @@ Bool ArenaStep(Globals globals, double interval, double multiplier)
arena = GlobalsArena(globals);
clocks_per_sec = ClocksPerSec();
start = ClockNow();
end = start + (Clock)(interval * clocks_per_sec);
AVER(end >= start);
stepped = FALSE;
if (PolicyShouldCollectWorld(arena, interval, multiplier,
start, clocks_per_sec))
{
Res res;
Trace trace;
res = TraceStartCollectAll(&trace, arena, TraceStartWhyOPPORTUNISM);
if (res == ResOK) {
arena->lastWorldCollect = start;
stepped = TRUE;
}
}
start = now = ClockNow();
intervalEnd = start + (Clock)(interval * clocks_per_sec);
AVER(intervalEnd >= start);
availableEnd = start + (Clock)(interval * multiplier * clocks_per_sec);
AVER(availableEnd >= start);
/* loop while there is work to do and time on the clock. */
do {
scanned = TracePoll(globals);
now = ClockNow();
if (scanned > 0) {
stepped = TRUE;
arena->tracedSize += scanned;
Trace trace;
if (arena->busyTraces != TraceSetEMPTY) {
trace = ArenaTrace(arena, (TraceId)0);
} else {
/* No traces are running: consider collecting the world. */
if (PolicyShouldCollectWorld(arena, availableEnd - now, now,
clocks_per_sec))
{
Res res;
res = TraceStartCollectAll(&trace, arena, TraceStartWhyOPPORTUNISM);
if (res != ResOK)
break;
arena->lastWorldCollect = now;
} else {
/* Not worth collecting the world; consider starting a trace. */
if (!PolicyStartTrace(&trace, arena))
break;
}
}
} while ((scanned > 0) && (now < end));
TraceAdvance(trace);
if (trace->state == TraceFINISHED)
TraceDestroyFinished(trace);
workWasDone = TRUE;
now = ClockNow();
} while (now < intervalEnd);
if (stepped) {
arena->tracedTime += (now - start) / (double) clocks_per_sec;
if (workWasDone) {
ArenaAccumulateTime(arena, start, now);
}
return stepped;
return workWasDone;
}
/* ArenaFinalize -- registers an object for finalization

View file

@ -394,12 +394,14 @@ extern Bool TraceIdCheck(TraceId id);
extern Bool TraceSetCheck(TraceSet ts);
extern Bool TraceCheck(Trace trace);
extern Res TraceCreate(Trace *traceReturn, Arena arena, int why);
extern void TraceDestroy(Trace trace);
extern void TraceDestroyInit(Trace trace);
extern void TraceDestroyFinished(Trace trace);
extern Bool TraceIsEmpty(Trace trace);
extern Res TraceAddWhite(Trace trace, Seg seg);
extern Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet);
extern Res TraceStart(Trace trace, double mortality, double finishingTime);
extern Size TracePoll(Globals globals);
extern Bool TracePoll(Work *workReturn, Globals globals);
extern Rank TraceRankForAccess(Arena arena, Seg seg);
extern void TraceSegAccess(Arena arena, Seg seg, AccessSet mode);
@ -570,6 +572,7 @@ extern Bool ArenaHasAddr(Arena arena, Addr addr);
extern Res ArenaAddrObject(Addr *pReturn, Arena arena, Addr addr);
extern void ArenaChunkInsert(Arena arena, Chunk chunk);
extern void ArenaChunkRemoved(Arena arena, Chunk chunk);
extern void ArenaAccumulateTime(Arena arena, Clock start, Clock now);
extern void ArenaSetEmergency(Arena arena, Bool emergency);
extern Bool ArenaEmergency(Arena arean);
@ -661,12 +664,11 @@ extern Res ArenaNoExtend(Arena arena, Addr base, Size size);
extern Res PolicyAlloc(Tract *tractReturn, Arena arena, LocusPref pref,
Size size, Pool pool);
extern Bool PolicyShouldCollectWorld(Arena arena, double interval,
double multiplier, Clock now,
Clock clocks_per_sec);
extern Bool PolicyShouldCollectWorld(Arena arena, double availableTime,
Clock now, Clock clocks_per_sec);
extern Bool PolicyStartTrace(Trace *traceReturn, Arena arena);
extern Bool PolicyPoll(Arena arena);
extern Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize);
extern Bool PolicyPollAgain(Arena arena, Bool moreWork, Work tracedWork);
/* Locus interface */

View file

@ -212,6 +212,8 @@ static void testInArena(mps_arena_class_t arena_class, mps_arg_s *arena_args,
mps_class_mfs(), args), "stress MFS");
} MPS_ARGS_END(args);
/* Manual allocation should not cause any garbage collections. */
Insist(mps_collections(arena) == 0);
mps_arena_destroy(arena);
}

View file

@ -486,7 +486,7 @@ typedef struct TraceStruct {
Size condemned; /* condemned bytes */
Size notCondemned; /* collectable but not condemned */
Size foundation; /* initial grey set size */
Size rate; /* segs to scan per increment */
Work quantumWork; /* tracing work to be done in each poll */
STATISTIC_DECL(Count greySegCount); /* number of grey segs */
STATISTIC_DECL(Count greySegMax); /* max number of grey segs */
STATISTIC_DECL(Count rootScanCount); /* number of roots scanned */
@ -777,7 +777,7 @@ typedef struct mps_arena_s {
TraceMessage tMessage[TraceLIMIT]; /* <design/message-gc/> */
/* policy fields */
double tracedSize;
double tracedWork;
double tracedTime;
Clock lastWorldCollect;

View file

@ -38,6 +38,7 @@ typedef Word Size; /* <design/type/#size> */
typedef Word Count; /* <design/type/#count> */
typedef Word Index; /* <design/type/#index> */
typedef Word Align; /* <design/type/#align> */
typedef Word Work; /* <design/type/#work> */
typedef unsigned Shift; /* <design/type/#shift> */
typedef unsigned Serial; /* <design/type/#serial> */
typedef Addr Ref; /* <design/type/#ref> */

View file

@ -144,9 +144,8 @@ static double policyCollectionTime(Arena arena)
collectableSize = ArenaCollectable(arena);
/* The condition arena->tracedTime >= 1.0 ensures that the division
* can't overflow. */
if (arena->tracedSize >= ARENA_MINIMUM_COLLECTABLE_SIZE
&& arena->tracedTime >= 1.0)
collectionRate = arena->tracedSize / arena->tracedTime;
if (arena->tracedTime >= 1.0)
collectionRate = arena->tracedWork / arena->tracedTime;
else
collectionRate = ARENA_DEFAULT_COLLECTION_RATE;
collectionTime = collectableSize / collectionRate;
@ -161,37 +160,41 @@ static double policyCollectionTime(Arena arena)
* Return TRUE if we should try collecting the world now, FALSE if
* not.
*
* This is the policy behind mps_arena_step, and so there the client
* must have provided us with be enough time to collect the world, and
* This is the policy behind mps_arena_step, and so the client
* must have provided us with enough time to collect the world, and
* enough time must have passed since the last time we did that
* opportunistically.
*/
Bool PolicyShouldCollectWorld(Arena arena, double interval, double multiplier,
Bool PolicyShouldCollectWorld(Arena arena, double availableTime,
Clock now, Clock clocks_per_sec)
{
/* don't collect the world if we're not given any time */
if ((interval > 0.0) && (multiplier > 0.0)) {
/* don't collect the world if we're already collecting. */
if (arena->busyTraces == TraceSetEMPTY) {
/* don't collect the world if it's very small */
Size collectableSize = ArenaCollectable(arena);
if (collectableSize > ARENA_MINIMUM_COLLECTABLE_SIZE) {
/* how long would it take to collect the world? */
double collectionTime = policyCollectionTime(arena);
Size collectableSize;
double collectionTime, sinceLastWorldCollect;
/* how long since we last collected the world? */
double sinceLastWorldCollect = ((now - arena->lastWorldCollect) /
(double) clocks_per_sec);
/* have to be offered enough time, and it has to be a long time
* since we last did it. */
if ((interval * multiplier > collectionTime) &&
sinceLastWorldCollect > collectionTime / ARENA_MAX_COLLECT_FRACTION)
return TRUE;
}
}
}
return FALSE;
AVERT(Arena, arena);
/* Can't collect the world if we're already collecting. */
AVER(arena->busyTraces == TraceSetEMPTY);
if (availableTime <= 0.0)
/* Can't collect the world if we're not given any time. */
return FALSE;
/* Don't collect the world if it's very small. */
collectableSize = ArenaCollectable(arena);
if (collectableSize < ARENA_MINIMUM_COLLECTABLE_SIZE)
return FALSE;
/* How long would it take to collect the world? */
collectionTime = policyCollectionTime(arena);
/* How long since we last collected the world? */
sinceLastWorldCollect = ((now - arena->lastWorldCollect) /
(double) clocks_per_sec);
/* Offered enough time, and long enough since we last did it? */
return availableTime > collectionTime
&& sinceLastWorldCollect > collectionTime / ARENA_MAX_COLLECT_FRACTION;
}
@ -322,6 +325,8 @@ Bool PolicyStartTrace(Trace *traceReturn, Arena arena)
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);
@ -333,11 +338,9 @@ Bool PolicyStartTrace(Trace *traceReturn, Arena arena)
} /* (dynamicDeferral > 0.0) */
return FALSE;
nothingCondemned:
failCondemn:
TraceDestroy(trace);
/* This is an unlikely case, but clear the emergency flag so the next attempt
starts normally. */
ArenaSetEmergency(arena, FALSE);
TraceDestroyInit(trace);
failStart:
return FALSE;
}
@ -364,20 +367,20 @@ Bool PolicyPoll(Arena arena)
* should return to the mutator.
*
* start is the clock time when the MPS was entered.
* tracedSize is the amount of work done by the last call to TracePoll.
* moreWork and tracedWork are the results of the last call to TracePoll.
*/
Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize)
Bool PolicyPollAgain(Arena arena, Bool moreWork, Work tracedWork)
{
Globals globals;
double nextPollThreshold;
AVERT(Arena, arena);
globals = ArenaGlobals(arena);
UNUSED(start);
UNUSED(tracedWork);
if (tracedSize == 0) {
/* No work was done. Sleep until NOW + a bit. */
if (!moreWork) {
/* No more work to do. Sleep until NOW + a bit. */
nextPollThreshold = globals->fillMutatorSize + ArenaPollALLOCTIME;
} else {
/* We did one quantum of work; consume one unit of 'time'. */
@ -388,7 +391,7 @@ Bool PolicyPollAgain(Arena arena, Clock start, Size tracedSize)
AVER(nextPollThreshold > globals->pollThreshold);
globals->pollThreshold = nextPollThreshold;
return PolicyPoll(arena);
return ArenaEmergency(arena) || PolicyPoll(arena);
}

View file

@ -620,6 +620,7 @@ static Res MRGRefSegScan(ScanState ss, MRGRefSeg refseg, MRG mrg)
MRGFinalize(arena, linkseg, i);
}
}
ss->scannedSize += sizeof *refPart;
}
}
} TRACE_SCAN_END(ss);

View file

@ -109,7 +109,7 @@ void ScanStateInit(ScanState ss, TraceSet ts, Arena arena,
STATISTIC(ss->preservedInPlaceCount = (Count)0);
ss->preservedInPlaceSize = (Size)0; /* see .message.data */
STATISTIC(ss->copiedSize = (Size)0);
ss->scannedSize = (Size)0; /* see .workclock */
ss->scannedSize = (Size)0; /* see .work */
ss->sig = ScanStateSig;
AVERT(ScanState, ss);
@ -158,6 +158,7 @@ Bool TraceCheck(Trace trace)
/* Use trace->state to check more invariants. */
switch(trace->state) {
case TraceINIT:
CHECKL(!TraceSetIsMember(trace->arena->flippedTraces, trace));
/* @@@@ What can be checked here? */
break;
@ -276,7 +277,7 @@ static void traceUpdateCounts(Trace trace, ScanState ss,
break;
}
case traceAccountingPhaseSegScan: {
trace->segScanSize += ss->scannedSize; /* see .workclock */
trace->segScanSize += ss->scannedSize; /* see .work */
trace->segCopiedSize += ss->copiedSize;
STATISTIC(++trace->segScanCount);
break;
@ -338,6 +339,15 @@ static ZoneSet traceSetWhiteUnion(TraceSet ts, Arena arena)
}
/* TraceIsEmpty -- return TRUE if trace has no condemned segments */
Bool TraceIsEmpty(Trace trace)
{
AVERT(Trace, trace);
return trace->condemned == 0;
}
/* TraceAddWhite -- add a segment to the white set of a trace */
Res TraceAddWhite(Trace trace, Seg seg)
@ -390,7 +400,6 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet)
Seg seg;
Arena arena;
Res res;
Bool haveWhiteSegs = FALSE;
AVERT(Trace, trace);
AVER(condemnedSet != ZoneSetEMPTY);
@ -417,7 +426,6 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet)
res = TraceAddWhite(trace, seg);
if(res != ResOK)
goto failBegin;
haveWhiteSegs = TRUE;
}
} while (SegNext(&seg, arena, seg));
}
@ -430,7 +438,7 @@ Res TraceCondemnZones(Trace trace, ZoneSet condemnedSet)
return ResOK;
failBegin:
AVER(!haveWhiteSegs); /* See .whiten.fail. */
AVER(TraceIsEmpty(trace)); /* See .whiten.fail. */
return res;
}
@ -693,14 +701,14 @@ found:
trace->condemned = (Size)0; /* nothing condemned yet */
trace->notCondemned = (Size)0;
trace->foundation = (Size)0; /* nothing grey yet */
trace->rate = (Size)0; /* no scanning to be done yet */
trace->quantumWork = (Work)0; /* computed in TraceStart */
STATISTIC(trace->greySegCount = (Count)0);
STATISTIC(trace->greySegMax = (Count)0);
STATISTIC(trace->rootScanCount = (Count)0);
trace->rootScanSize = (Size)0;
trace->rootCopiedSize = (Size)0;
STATISTIC(trace->segScanCount = (Count)0);
trace->segScanSize = (Size)0; /* see .workclock */
trace->segScanSize = (Size)0; /* see .work */
trace->segCopiedSize = (Size)0;
STATISTIC(trace->singleScanCount = (Count)0);
STATISTIC(trace->singleScanSize = (Size)0);
@ -755,7 +763,25 @@ found:
}
/* TraceDestroy -- destroy a trace object
/* TraceDestroyInit -- destroy a trace object in state INIT */
void TraceDestroyInit(Trace trace)
{
AVERT(Trace, trace);
AVER(trace->state == TraceINIT);
AVER(trace->condemned == 0);
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);
}
/* TraceDestroyFinished -- destroy a trace object in state FINISHED
*
* Finish and deallocate a Trace object, freeing up a TraceId.
*
@ -764,7 +790,7 @@ found:
* etc. would need to be reset to black. This also means the error
* paths in this file don't work. @@@@ */
void TraceDestroy(Trace trace)
void TraceDestroyFinished(Trace trace)
{
AVERT(Trace, trace);
AVER(trace->state == TraceFINISHED);
@ -809,6 +835,9 @@ void TraceDestroy(Trace 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);
}
@ -1457,7 +1486,6 @@ static Res traceCondemnAll(Trace trace)
Res res;
Arena arena;
Ring poolNode, nextPoolNode, chainNode, nextChainNode;
Bool haveWhiteSegs = FALSE;
arena = trace->arena;
AVERT(Arena, arena);
@ -1476,11 +1504,13 @@ static Res traceCondemnAll(Trace trace)
res = TraceAddWhite(trace, seg);
if (res != ResOK)
goto failBegin;
haveWhiteSegs = TRUE;
}
}
}
if (TraceIsEmpty(trace))
return ResFAIL;
/* Notify all the chains. */
RING_FOR(chainNode, &arena->chainRing, nextChainNode) {
Chain chain = RING_ELT(Chain, chainRing, chainNode);
@ -1497,7 +1527,7 @@ failBegin:
* pool class that fails to whiten a segment, then this assertion
* will be triggered. In that case, we'll have to recover here by
* blackening the segments again. */
AVER(!haveWhiteSegs);
AVER(TraceIsEmpty(trace));
return res;
}
@ -1556,6 +1586,7 @@ Res TraceStart(Trace trace, double mortality, double finishingTime)
AVER(0.0 <= mortality);
AVER(mortality <= 1.0);
AVER(finishingTime >= 0.0);
AVER(trace->condemned > 0);
arena = trace->arena;
@ -1616,8 +1647,10 @@ Res TraceStart(Trace trace, double mortality, double finishingTime)
/* integer, so try to make sure it fits. */
if(nPolls >= (double)LONG_MAX)
nPolls = (double)LONG_MAX;
/* rate equals scanning work per number of polls available */
trace->rate = (trace->foundation + sSurvivors) / (unsigned long)nPolls + 1;
/* One quantum of work equals total tracing work divided by number
* of polls, plus one to ensure it's not zero. */
trace->quantumWork
= (trace->foundation + sSurvivors) / (unsigned long)nPolls + 1;
}
/* TODO: compute rate of scanning here. */
@ -1625,11 +1658,11 @@ Res TraceStart(Trace trace, double mortality, double finishingTime)
EVENT8(TraceStart, trace, mortality, finishingTime,
trace->condemned, trace->notCondemned,
trace->foundation, trace->white,
trace->rate);
trace->quantumWork);
STATISTIC_STAT(EVENT7(TraceStatCondemn, trace,
trace->condemned, trace->notCondemned,
trace->foundation, trace->rate,
trace->foundation, trace->quantumWork,
mortality, finishingTime));
trace->state = TraceUNFLIPPED;
@ -1640,14 +1673,24 @@ Res TraceStart(Trace trace, double mortality, double finishingTime)
}
/* traceWork -- a measure of the work done for this trace.
*
* See design.mps.type.work.
*/
#define traceWork(trace) ((Work)((trace)->segScanSize + (trace)->rootScanSize))
/* TraceAdvance -- progress a trace by one step */
void TraceAdvance(Trace trace)
{
Arena arena;
Work oldWork, newWork;
AVERT(Trace, trace);
arena = trace->arena;
oldWork = traceWork(trace);
switch (trace->state) {
case TraceUNFLIPPED:
@ -1676,6 +1719,10 @@ void TraceAdvance(Trace trace)
NOTREACHED;
break;
}
newWork = traceWork(trace);
AVER(newWork >= oldWork);
arena->tracedWork += newWork - oldWork;
}
@ -1720,32 +1767,24 @@ failStart:
if the assertion isn't hit, so drop through anyway. */
NOTREACHED;
failCondemn:
TraceDestroy(trace);
/* We don't know how long it'll be before another collection. Make sure
the next one starts in normal mode. */
ArenaSetEmergency(arena, FALSE);
TraceDestroyInit(trace);
return res;
}
/* traceWorkClock -- a measure of the work done for this trace
*
* .workclock: Segment and root scanning work is the regulator. */
#define traceWorkClock(trace) ((trace)->segScanSize + (trace)->rootScanSize)
/* TracePoll -- Check if there's any tracing work to be done
*
* Consider starting a trace if none is running; advance the running
* trace (if any) by one quantum. Return a measure of the work done.
* trace (if any) by one quantum. If there may be more work to do,
* update *workReturn with a measure of the work done and return TRUE.
* Otherwise return FALSE.
*/
Size TracePoll(Globals globals)
Bool TracePoll(Work *workReturn, Globals globals)
{
Trace trace;
Arena arena;
Size oldScannedSize, scannedSize, pollEnd;
Work oldWork, newWork, work, endWork;
AVERT(Globals, globals);
arena = GlobalsArena(globals);
@ -1755,24 +1794,22 @@ Size TracePoll(Globals globals)
} else {
/* No traces are running: consider starting one now. */
if (!PolicyStartTrace(&trace, arena))
return (Size)0;
return FALSE;
}
AVER(arena->busyTraces == TraceSetSingle(trace));
oldScannedSize = traceWorkClock(trace);
pollEnd = oldScannedSize + trace->rate;
oldWork = traceWork(trace);
endWork = oldWork + trace->quantumWork;
do {
TraceAdvance(trace);
} while (trace->state != TraceFINISHED
&& (ArenaEmergency(arena) || traceWorkClock(trace) < pollEnd));
scannedSize = traceWorkClock(trace) - oldScannedSize;
if (trace->state == TraceFINISHED) {
TraceDestroy(trace);
/* A trace finished, and hopefully reclaimed some memory, so clear any
* emergency. */
ArenaSetEmergency(arena, FALSE);
}
return scannedSize;
} while (trace->state != TraceFINISHED && traceWork(trace) < endWork);
newWork = traceWork(trace);
AVER(newWork >= oldWork);
work = newWork - oldWork;
if (trace->state == TraceFINISHED)
TraceDestroyFinished(trace);
*workReturn = work;
return TRUE;
}
@ -1810,7 +1847,7 @@ Res TraceDescribe(Trace trace, mps_lib_FILE *stream, Count depth)
" condemned $U\n", (WriteFU)trace->condemned,
" notCondemned $U\n", (WriteFU)trace->notCondemned,
" foundation $U\n", (WriteFU)trace->foundation,
" rate $U\n", (WriteFU)trace->rate,
" quantumWork $U\n", (WriteFU)trace->quantumWork,
" rootScanSize $U\n", (WriteFU)trace->rootScanSize,
" rootCopiedSize $U\n", (WriteFU)trace->rootCopiedSize,
" segScanSize $U\n", (WriteFU)trace->segScanSize,

View file

@ -572,25 +572,28 @@ void ArenaPark(Globals globals)
TraceId ti;
Trace trace;
Arena arena;
Clock start;
AVERT(Globals, globals);
arena = GlobalsArena(globals);
globals->clamped = TRUE;
start = ClockNow();
while(arena->busyTraces != TraceSetEMPTY) {
/* Advance all active traces. */
TRACE_SET_ITER(ti, trace, arena->busyTraces, arena)
TraceAdvance(trace);
if(trace->state == TraceFINISHED) {
TraceDestroy(trace);
TraceDestroyFinished(trace);
}
TRACE_SET_ITER_END(ti, trace, arena->busyTraces, arena);
}
/* Clear any emergency flag so that the next collection starts normally.
Any traces that have been finished may have reclaimed memory. */
ArenaSetEmergency(arena, FALSE);
ArenaAccumulateTime(arena, start, ClockNow());
/* All traces have finished so there must not be an emergency. */
AVER(!ArenaEmergency(arena));
}
/* ArenaStartCollect -- start a collection of everything in the

View file

@ -359,7 +359,7 @@ static Res ArenaRootsWalk(Globals arenaGlobals, mps_roots_stepper_t f,
rootsStepClosureFinish(rsc);
/* Make this trace look like any other finished trace. */
trace->state = TraceFINISHED;
TraceDestroy(trace);
TraceDestroyFinished(trace);
AVER(!ArenaEmergency(arena)); /* There was no allocation. */
return res;

View file

@ -456,22 +456,21 @@ for example job003898_.
Deciding whether to collect the world
.....................................
``Bool PolicyShouldCollectWorld(Arena arena, double interval, double multiplier, Clock now, Clock clocks_per_sec)``
``Bool PolicyShouldCollectWorld(Arena arena, double availableTime, Clock now, Clock clocks_per_sec)``
_`.policy.world`: Determine whether now is a good time for
``mps_arena_step()`` to start a collection of the world. Return
``TRUE`` if so, ``FALSE`` if not. The ``interval`` and ``multiplier``
arguments are the ones the client program passed to
``mps_arena_step()``, ``now`` is the current time as returned by
``ClockNow()``, and ``clocks_per_sec`` is the result of calling
``ClocksPerSec()``.
``TRUE`` if so, ``FALSE`` if not. The ``availableTime`` argument is an
estimate of the time that's available for the collection, ``now`` is
the current time as returned by ``ClockNow()``, and ``clocks_per_sec``
is the result of calling ``ClocksPerSec()``.
_`.policy.world.impl`: There are two conditions: the client program's
estimate of the available time must be enough to complete the
collection, and the last collection of the world must be long enough
in the past that the ``mps_arena_step()`` won't be spending more than
a certain fraction of runtime in collections. (This fraction is given
by the ``ARENA_MAX_COLLECT_FRACTION`` configuration parameter.)
_`.policy.world.impl`: There are two conditions: the estimate of the
available time must be enough to complete the collection, and the last
collection of the world must be long enough in the past that the
``mps_arena_step()`` won't be spending more than a certain fraction of
runtime in collections. (This fraction is given by the
``ARENA_MAX_COLLECT_FRACTION`` configuration parameter.)

View file

@ -664,6 +664,17 @@ _`.word.ops`: ``WordIsAligned()``, ``WordAlignUp()``,
``WordAlignDown()`` and ``WordRoundUp()``.
``typedef MPS_T_WORD Work``
_`.work`: ``Work`` is an unsigned integral type representing
accumulated work done by the collector.
_`.work.impl`: Work is implemented as a count of the bytes scanned by
the collector in segments and roots. This is a very crude measure,
because it depends on the scanning functions supplied by the mutator,
which we know very little about.
``typedef Word ZoneSet``
_`.zoneset`: ``ZoneSet`` is a conservative approximation to a set of