diff --git a/mps/code/apss.c b/mps/code/apss.c index 5192baada42..79a029f86b1 100644 --- a/mps/code/apss.c +++ b/mps/code/apss.c @@ -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); } diff --git a/mps/code/arena.c b/mps/code/arena.c index 860715c5059..dc7ae37b15c 100644 --- a/mps/code/arena.c +++ b/mps/code/arena.c @@ -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(); } diff --git a/mps/code/config.h b/mps/code/config.h index 11d5a943a04..d18076ec2e4 100644 --- a/mps/code/config.h +++ b/mps/code/config.h @@ -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 ), for + * use in the case where there isn't enough data to use a measured + * value. */ #define ARENA_DEFAULT_COLLECTION_RATE (25000000.0) diff --git a/mps/code/eventdef.h b/mps/code/eventdef.h index 081b45de7c2..312f257ee0f 100644 --- a/mps/code/eventdef.h +++ b/mps/code/eventdef.h @@ -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 */ \ diff --git a/mps/code/global.c b/mps/code/global.c index 354adb54a9a..39e83714f82 100644 --- a/mps/code/global.c +++ b/mps/code/global.c @@ -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; /* */ arena->flippedTraces = TraceSetEMPTY; /* */ - arena->tracedSize = 0.0; + arena->tracedWork = 0.0; arena->tracedTime = 0.0; arena->lastWorldCollect = ClockNow(); arena->insideShield = FALSE; /* */ @@ -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 diff --git a/mps/code/mpm.h b/mps/code/mpm.h index 118d8ff070e..6cbc676ab00 100644 --- a/mps/code/mpm.h +++ b/mps/code/mpm.h @@ -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 */ diff --git a/mps/code/mpmss.c b/mps/code/mpmss.c index 2c746b7e34d..31616425a65 100644 --- a/mps/code/mpmss.c +++ b/mps/code/mpmss.c @@ -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); } diff --git a/mps/code/mpmst.h b/mps/code/mpmst.h index 8b76dec162a..4e848f5da57 100644 --- a/mps/code/mpmst.h +++ b/mps/code/mpmst.h @@ -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]; /* */ /* policy fields */ - double tracedSize; + double tracedWork; double tracedTime; Clock lastWorldCollect; diff --git a/mps/code/mpmtypes.h b/mps/code/mpmtypes.h index 7232f081a39..a0774b9b6b3 100644 --- a/mps/code/mpmtypes.h +++ b/mps/code/mpmtypes.h @@ -38,6 +38,7 @@ typedef Word Size; /* */ typedef Word Count; /* */ typedef Word Index; /* */ typedef Word Align; /* */ +typedef Word Work; /* */ typedef unsigned Shift; /* */ typedef unsigned Serial; /* */ typedef Addr Ref; /* */ diff --git a/mps/code/policy.c b/mps/code/policy.c index 134b8236013..a9bc710be1b 100644 --- a/mps/code/policy.c +++ b/mps/code/policy.c @@ -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); } diff --git a/mps/code/poolmrg.c b/mps/code/poolmrg.c index 2cb5e7a2f21..feb18436e12 100644 --- a/mps/code/poolmrg.c +++ b/mps/code/poolmrg.c @@ -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); diff --git a/mps/code/trace.c b/mps/code/trace.c index 5ce3e3617ad..6d9cb0f0f98 100644 --- a/mps/code/trace.c +++ b/mps/code/trace.c @@ -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, diff --git a/mps/code/traceanc.c b/mps/code/traceanc.c index c57500c0b65..4730c7a0f54 100644 --- a/mps/code/traceanc.c +++ b/mps/code/traceanc.c @@ -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 diff --git a/mps/code/walk.c b/mps/code/walk.c index 38278cce1ad..e4542359469 100644 --- a/mps/code/walk.c +++ b/mps/code/walk.c @@ -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; diff --git a/mps/design/strategy.txt b/mps/design/strategy.txt index 6e0472b04f5..4d989435e88 100644 --- a/mps/design/strategy.txt +++ b/mps/design/strategy.txt @@ -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.) diff --git a/mps/design/type.txt b/mps/design/type.txt index 25943f41a4b..5f546e1e32e 100644 --- a/mps/design/type.txt +++ b/mps/design/type.txt @@ -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