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:
commit
314193d8c2
16 changed files with 241 additions and 157 deletions
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
||||
|
|
|
|||
|
|
@ -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 */ \
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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> */
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
125
mps/code/trace.c
125
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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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.)
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue