Option 0 for step multiplier makes GC non-incremental

This commit is contained in:
Roberto Ierusalimschy 2023-12-20 11:06:27 -03:00
parent 4eda1acafa
commit 666e95a66d
6 changed files with 84 additions and 50 deletions

67
lgc.c
View File

@ -93,6 +93,7 @@
*/
#define markobjectN(g,t) { if (t) markobject(g,t); }
static void reallymarkobject (global_State *g, GCObject *o);
static l_obj atomic (lua_State *L);
static void entersweep (lua_State *L);
@ -831,10 +832,10 @@ static void freeobj (lua_State *L, GCObject *o) {
** for next collection cycle. Return where to continue the traversal or
** NULL if list is finished.
*/
static GCObject **sweeplist (lua_State *L, GCObject **p, int countin) {
static GCObject **sweeplist (lua_State *L, GCObject **p, l_obj countin) {
global_State *g = G(L);
int ow = otherwhite(g);
int i;
l_obj i;
int white = luaC_white(g); /* current white */
for (i = 0; *p != NULL && i < countin; i++) {
GCObject *curr = *p;
@ -1357,8 +1358,8 @@ static void setminordebt (global_State *g) {
** collection.
*/
static void entergen (lua_State *L, global_State *g) {
luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
luaC_runtilstate(L, GCSpause, 1); /* prepare to start a new cycle */
luaC_runtilstate(L, GCSpropagate, 1); /* start new cycle */
atomic(L); /* propagates all and then do the atomic stuff */
atomic2gen(L, g);
setminordebt(g); /* set debt assuming next cycle will be minor */
@ -1515,10 +1516,14 @@ static l_obj atomic (lua_State *L) {
}
/*
** Do a sweep step. The normal case (not fast) sweeps at most GCSWEEPMAX
** elements. The fast case sweeps the whole list.
*/
static void sweepstep (lua_State *L, global_State *g,
int nextstate, GCObject **nextlist) {
int nextstate, GCObject **nextlist, int fast) {
if (g->sweepgc)
g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX);
g->sweepgc = sweeplist(L, g->sweepgc, fast ? MAX_LOBJ : GCSWEEPMAX);
else { /* enter next state */
g->gcstate = nextstate;
g->sweepgc = nextlist;
@ -1526,7 +1531,19 @@ static void sweepstep (lua_State *L, global_State *g,
}
static l_obj singlestep (lua_State *L) {
/*
** Performs one incremental "step" in an incremental garbage collection.
** For indivisible work, a step goes to the next state. When marking
** (propagating), a step traverses one object. When sweeping, a step
** sweeps GCSWEEPMAX objects, to avoid a big overhead for sweeping
** objects one by one. (Sweeping is inexpensive, no matter the
** object.) When 'fast' is true, 'singlestep' tries to finish a state
** "as fast as possible". In particular, it skips the propagation
** phase and leaves all objects to be traversed by the atomic phase:
** That avoids traversing twice some objects, such as theads and
** weak tables.
*/
static l_obj singlestep (lua_State *L, int fast) {
global_State *g = G(L);
l_obj work;
lua_assert(!g->gcstopem); /* collector is not reentrant */
@ -1539,7 +1556,7 @@ static l_obj singlestep (lua_State *L) {
break;
}
case GCSpropagate: {
if (g->gray == NULL) { /* no more gray objects? */
if (fast || g->gray == NULL) {
g->gcstate = GCSenteratomic; /* finish propagate phase */
work = 0;
}
@ -1556,17 +1573,17 @@ static l_obj singlestep (lua_State *L) {
break;
}
case GCSswpallgc: { /* sweep "regular" objects */
sweepstep(L, g, GCSswpfinobj, &g->finobj);
sweepstep(L, g, GCSswpfinobj, &g->finobj, fast);
work = GCSWEEPMAX;
break;
}
case GCSswpfinobj: { /* sweep objects with finalizers */
sweepstep(L, g, GCSswptobefnz, &g->tobefnz);
sweepstep(L, g, GCSswptobefnz, &g->tobefnz, fast);
work = GCSWEEPMAX;
break;
}
case GCSswptobefnz: { /* sweep objects to be finalized */
sweepstep(L, g, GCSswpend, NULL);
sweepstep(L, g, GCSswpend, NULL, fast);
work = GCSWEEPMAX;
break;
}
@ -1596,14 +1613,15 @@ static l_obj singlestep (lua_State *L) {
/*
** advances the garbage collector until it reaches a state allowed
** by 'statemask'
** Advances the garbage collector until it reaches the given state.
** (The option 'fast' is only for testing; in normal code, 'fast'
** here is always true.)
*/
void luaC_runtilstate (lua_State *L, int statesmask) {
void luaC_runtilstate (lua_State *L, int state, int fast) {
global_State *g = G(L);
lua_assert(g->gckind == KGC_INC);
while (!testbit(statesmask, g->gcstate))
singlestep(L);
while (state != g->gcstate)
singlestep(L, fast);
}
@ -1618,8 +1636,13 @@ void luaC_runtilstate (lua_State *L, int statesmask) {
static void incstep (lua_State *L, global_State *g) {
l_obj stepsize = cast(l_obj, 1) << g->gcstepsize;
l_obj work2do = applygcparam(g, gcstepmul, stepsize);
do { /* repeat until pause or enough "credit" (negative debt) */
l_obj work = singlestep(L); /* perform one single step */
int fast = 0;
if (work2do == 0) { /* special case: do a full collection */
work2do = MAX_LOBJ; /* do unlimited work */
fast = 1;
}
do { /* repeat until pause or enough work */
l_obj work = singlestep(L, fast); /* perform one single step */
if (g->gckind == KGC_GENMINOR) /* returned to minor collections? */
return; /* nothing else to be done here */
work2do -= work;
@ -1665,13 +1688,11 @@ static void fullinc (lua_State *L, global_State *g) {
if (keepinvariant(g)) /* black objects? */
entersweep(L); /* sweep everything to turn them back to white */
/* finish any pending sweep phase to start a new cycle */
luaC_runtilstate(L, bitmask(GCSpause));
luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
g->gcstate = GCSenteratomic; /* go straight to atomic phase ??? */
luaC_runtilstate(L, bitmask(GCScallfin)); /* run up to finalizers */
luaC_runtilstate(L, GCSpause, 1);
luaC_runtilstate(L, GCScallfin, 1); /* run up to finalizers */
/* 'marked' must be correct after a full GC cycle */
lua_assert(g->marked == gettotalobjs(g));
luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
luaC_runtilstate(L, GCSpause, 1); /* finish collection */
setpause(g);
}

12
lgc.h
View File

@ -182,12 +182,14 @@
/* incremental */
/* wait memory to double before starting new cycle */
#define LUAI_GCPAUSE 200
/* Number of objects must be LUAI_GCPAUSE% before starting new cycle */
#define LUAI_GCPAUSE 300
#define LUAI_GCMUL 300 /* step multiplier */
/* Step multiplier. (Roughly, the collector handles LUAI_GCMUL% objects
for each new allocated object.) */
#define LUAI_GCMUL 200
/* how many objects to allocate before next GC step (log2) */
/* How many objects to allocate before next GC step (log2) */
#define LUAI_GCSTEPSIZE 8 /* 256 objects */
@ -244,7 +246,7 @@
LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
LUAI_FUNC void luaC_freeallobjects (lua_State *L);
LUAI_FUNC void luaC_step (lua_State *L);
LUAI_FUNC void luaC_runtilstate (lua_State *L, int statesmask);
LUAI_FUNC void luaC_runtilstate (lua_State *L, int state, int fast);
LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
LUAI_FUNC GCObject *luaC_newobjdt (lua_State *L, int tt, size_t sz,

View File

@ -33,7 +33,8 @@ typedef unsigned long lu_mem;
typedef long l_obj;
#endif /* } */
#define MAX_LOBJ cast(l_obj, ~cast(lu_mem, 0) >> 1)
#define MAX_LOBJ \
cast(l_obj, (cast(lu_mem, 1) << (sizeof(l_obj) * CHAR_BIT - 1)) - 1)
/* chars used as small naturals (so that 'char' is reserved for characters) */
@ -44,7 +45,10 @@ typedef signed char ls_byte;
/* maximum value for size_t */
#define MAX_SIZET ((size_t)(~(size_t)0))
/* maximum size visible for Lua (must be representable in a lua_Integer) */
/*
** Maximum size for strings and userdata visible for Lua (should be
** representable in a lua_Integer)
*/
#define MAX_SIZE (sizeof(size_t) < sizeof(lua_Integer) ? MAX_SIZET \
: (size_t)(LUA_MAXINTEGER))

View File

@ -941,10 +941,10 @@ static int gc_printobj (lua_State *L) {
static int gc_state (lua_State *L) {
static const char *statenames[] = {
"propagate", "atomic", "enteratomic", "sweepallgc", "sweepfinobj",
"propagate", "atomic", "sweepallgc", "sweepfinobj",
"sweeptobefnz", "sweepend", "callfin", "pause", ""};
static const int states[] = {
GCSpropagate, GCSenteratomic, GCSatomic, GCSswpallgc, GCSswpfinobj,
GCSpropagate, GCSenteratomic, GCSswpallgc, GCSswpfinobj,
GCSswptobefnz, GCSswpend, GCScallfin, GCSpause, -1};
int option = states[luaL_checkoption(L, 1, "", statenames)];
if (option == -1) {
@ -957,9 +957,9 @@ static int gc_state (lua_State *L) {
luaL_error(L, "cannot change states in generational mode");
lua_lock(L);
if (option < g->gcstate) { /* must cross 'pause'? */
luaC_runtilstate(L, bitmask(GCSpause)); /* run until pause */
luaC_runtilstate(L, GCSpause, 1); /* run until pause */
}
luaC_runtilstate(L, bitmask(option));
luaC_runtilstate(L, option, 0); /* do not skip propagation state */
lua_assert(G(L)->gcstate == option);
lua_unlock(L);
return 0;

View File

@ -664,7 +664,7 @@ Values equal to or less than 100 mean the collector will not wait to
start a new cycle.
A value of 200 means that the collector waits for
the total number of objects to double before starting a new cycle.
The default value is 200; the maximum value is 1000.
The default value is 300; the maximum value is 1000.
The garbage-collector step multiplier
controls the speed of the collector relative to
@ -674,7 +674,9 @@ how many objects it marks or sweeps for each object created.
Larger values make the collector more aggressive.
Beware that values too small can
make the collector too slow to ever finish a cycle.
The default value is 300; the maximum value is 1000.
The default value is 200; the maximum value is 1000.
As a special case, a zero value means unlimited work,
effectively producing a non-incremental, stop-the-world collector.
The garbage-collector step size controls the
size of each incremental step,
@ -682,9 +684,7 @@ specifically how many objects the interpreter creates
before performing a step.
This parameter is logarithmic:
A value of @M{n} means the interpreter will create @M{2@sp{n}}
objects between steps and perform equivalent work during the step.
A large value (e.g., 60) makes the collector a stop-the-world
(non-incremental) collector.
objects between steps.
The default value is 8,
which means steps of approximately @N{256 objects}.
@ -3306,43 +3306,43 @@ For options that need extra arguments,
they are listed after the option.
@description{
@item{@id{LUA_GCCOLLECT}|
@item{@defid{LUA_GCCOLLECT}|
Performs a full garbage-collection cycle.
}
@item{@id{LUA_GCSTOP}|
@item{@defid{LUA_GCSTOP}|
Stops the garbage collector.
}
@item{@id{LUA_GCRESTART}|
@item{@defid{LUA_GCRESTART}|
Restarts the garbage collector.
}
@item{@id{LUA_GCCOUNT}|
@item{@defid{LUA_GCCOUNT}|
Returns the current amount of memory (in Kbytes) in use by Lua.
}
@item{@id{LUA_GCCOUNTB}|
@item{@defid{LUA_GCCOUNTB}|
Returns the remainder of dividing the current amount of bytes of
memory in use by Lua by 1024.
}
@item{@id{LUA_GCSTEP}|
@item{@defid{LUA_GCSTEP}|
Performs a step of garbage collection.
}
@item{@id{LUA_GCISRUNNING}|
@item{@defid{LUA_GCISRUNNING}|
Returns a boolean that tells whether the collector is running
(i.e., not stopped).
}
@item{@id{LUA_GCINC} (int pause, int stepmul, int stepsize)|
@item{@defid{LUA_GCINC} (int pause, int stepmul, int stepsize)|
Changes the collector to incremental mode
with the given parameters @see{incmode}.
Returns the previous mode (@id{LUA_GCGEN} or @id{LUA_GCINC}).
}
@item{@id{LUA_GCGEN} (int minormul, int minormajor, int majorminor)|
@item{@defid{LUA_GCGEN} (int minormul, int minormajor, int majorminor)|
Changes the collector to generational mode
with the given parameters @see{genmode}.
Returns the previous mode (@id{LUA_GCGEN} or @id{LUA_GCINC}).

View File

@ -162,9 +162,16 @@ end
assert(collectgarbage'isrunning')
do print"testing stop-the-world collection"
collectgarbage("incremental", nil, 0)
-- just to make sure
assert(collectgarbage'isrunning')
-- each step does a complete cycle
assert(collectgarbage("step"))
assert(collectgarbage("step"))
-- back to default value
collectgarbage("incremental", nil, 200)
end
collectgarbage(oldmode)