diff --git a/lapi.c b/lapi.c index 71b679aa..374b3872 100644 --- a/lapi.c +++ b/lapi.c @@ -1204,26 +1204,29 @@ LUA_API int lua_gc (lua_State *L, int what, ...) { break; } case LUA_GCGEN: { - unsigned int minormul = va_arg(argp, unsigned int); - unsigned int majormul = va_arg(argp, unsigned int); + int minormul = va_arg(argp, int); + int minormajor = va_arg(argp, int); + int majorminor = va_arg(argp, int); res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN; - if (minormul != 0) + if (minormul >= 0) setgcparam(g, genminormul, minormul); - if (majormul != 0) - setgcparam(g, genmajormul, majormul); + if (minormajor >= 0) + setgcparam(g, minormajor, minormajor); + if (majorminor >= 0) + setgcparam(g, majorminor, majorminor); luaC_changemode(L, KGC_GENMINOR); break; } case LUA_GCINC: { - unsigned int pause = va_arg(argp, unsigned int); - unsigned int stepmul = va_arg(argp, unsigned int); - unsigned int stepsize = va_arg(argp, unsigned int); + int pause = va_arg(argp, int); + int stepmul = va_arg(argp, int); + int stepsize = va_arg(argp, int); res = (g->gckind == KGC_INC) ? LUA_GCINC : LUA_GCGEN; - if (pause != 0) + if (pause >= 0) setgcparam(g, gcpause, pause); - if (stepmul != 0) + if (stepmul >= 0) setgcparam(g, gcstepmul, stepmul); - if (stepsize != 0) + if (stepsize >= 0) g->gcstepsize = (stepsize <= log2maxs(l_obj)) ? stepsize : log2maxs(l_obj); luaC_changemode(L, KGC_INC); diff --git a/lbaselib.c b/lbaselib.c index 54a6c02d..9ad84dcf 100644 --- a/lbaselib.c +++ b/lbaselib.c @@ -224,14 +224,15 @@ static int luaB_collectgarbage (lua_State *L) { return 1; } case LUA_GCGEN: { - int minormul = (int)luaL_optinteger(L, 2, 0); - int majormul = (int)luaL_optinteger(L, 3, 0); - return pushmode(L, lua_gc(L, o, minormul, majormul)); + int minormul = (int)luaL_optinteger(L, 2, -1); + int majorminor = (int)luaL_optinteger(L, 3, -1); + int minormajor = (int)luaL_optinteger(L, 4, -1); + return pushmode(L, lua_gc(L, o, minormul, majorminor, minormajor)); } case LUA_GCINC: { - int pause = (int)luaL_optinteger(L, 2, 0); - int stepmul = (int)luaL_optinteger(L, 3, 0); - int stepsize = (int)luaL_optinteger(L, 4, 0); + int pause = (int)luaL_optinteger(L, 2, -1); + int stepmul = (int)luaL_optinteger(L, 3, -1); + int stepsize = (int)luaL_optinteger(L, 4, -1); return pushmode(L, lua_gc(L, o, pause, stepmul, stepsize)); } default: { diff --git a/lgc.c b/lgc.c index 50e6e0e9..a4f63765 100644 --- a/lgc.c +++ b/lgc.c @@ -1205,20 +1205,21 @@ static void correctgraylists (global_State *g) { /* ** Mark black 'OLD1' objects when starting a new young collection. ** Gray objects are already in some gray list, and so will be visited in -** the atomic step. The counter 'GCmajorminor' keeps how many objects to -** become old before a major collection. +** the atomic step. Returns the number of objects that became old. */ -static void markold (global_State *g, GCObject *from, GCObject *to) { +static l_obj markold (global_State *g, GCObject *from, GCObject *to) { GCObject *p; + l_obj count = 0; for (p = from; p != to; p = p->next) { if (getage(p) == G_OLD1) { lua_assert(!iswhite(p)); setage(p, G_OLD); /* now they are old */ - g->GCmajorminor--; /* one more old object */ + count++; /* one more old object */ if (isblack(p)) reallymarkobject(g, p); } } + return count; } @@ -1240,7 +1241,7 @@ static void finishgencycle (lua_State *L, global_State *g) { */ static void atomic2major (lua_State *L, global_State *g) { l_obj stepsize = cast(l_obj, 1) << g->gcstepsize; - g->GCmajorminor = gettotalobjs(g); + g->GCmajorminor = g->marked; /* number of live objects */ g->gckind = KGC_GENMAJOR; g->reallyold = g->old1 = g->survival = NULL; g->finobjrold = g->finobjold1 = g->finobjsur = NULL; @@ -1249,30 +1250,54 @@ static void atomic2major (lua_State *L, global_State *g) { } +/* +** Decide whether to shift to major mode. It tests two conditions: +** 1) Whether the number of added old objects in this collection is more +** than half the number of new objects. ("step" is the number of objects +** created between minor collections. Except for forward barriers, it +** is the maximum number of objects that can become old in each minor +** collection.) +** 2) Whether the accumulated number of added old objects is larger +** than 'minormajor'% of the number of lived objects after the last +** major collection. (That percentage is computed in 'limit'.) +*/ +static int checkminormajor (lua_State *L, global_State *g, l_obj addedold1) { + l_obj step = applygcparam(g, genminormul, g->GCmajorminor); + l_obj limit = applygcparam(g, minormajor, g->GCmajorminor); +//printf("-> major? %ld %ld %ld %ld (%ld)\n", g->marked, limit, step, addedold1, gettotalobjs(g)); + if (addedold1 >= (step >> 1) || g->marked >= limit) { + atomic2major(L, g); /* go to major mode */ + return 1; + } + return 0; /* stay in minor mode */ +} + /* ** Does a young collection. First, mark 'OLD1' objects. Then does the ** atomic step. Then, check whether to continue in minor mode. If so, ** sweep all lists and advance pointers. Finally, finish the collection. */ static void youngcollection (lua_State *L, global_State *g) { + l_obj addedold1 = 0; + l_obj marked = g->marked; /* preserve 'g->marked' */ GCObject **psurvival; /* to point to first non-dead survival object */ GCObject *dummy; /* dummy out parameter to 'sweepgen' */ lua_assert(g->gcstate == GCSpropagate); - g->marked = 0; if (g->firstold1) { /* are there regular OLD1 objects? */ - markold(g, g->firstold1, g->reallyold); /* mark them */ + addedold1 += markold(g, g->firstold1, g->reallyold); /* mark them */ g->firstold1 = NULL; /* no more OLD1 objects (for now) */ } - markold(g, g->finobj, g->finobjrold); - markold(g, g->tobefnz, NULL); + addedold1 += markold(g, g->finobj, g->finobjrold); + addedold1 += markold(g, g->tobefnz, NULL); - atomic(L); + atomic(L); /* will lose 'g->marked' */ + + /* keep total number of added old1 objects */ + g->marked = marked + addedold1; /* decide whether to shift to major mode */ - if (g->GCmajorminor <= 0) { /* ?? */ - atomic2major(L, g); /* go to major mode */ + if (checkminormajor(L, g, addedold1)) return; /* nothing else to be done here */ - } /* sweep nursery and get a pointer to its last live element */ g->gcstate = GCSswpallgc; @@ -1319,7 +1344,8 @@ static void atomic2gen (lua_State *L, global_State *g) { sweep2old(L, &g->tobefnz); g->gckind = KGC_GENMINOR; - g->GCmajorminor = applygcparam(g, genmajormul, g->marked); + g->GCmajorminor = g->marked; /* "base" for number of objects */ + g->marked = 0; /* to count the number of added old1 objects */ finishgencycle(L, g); } @@ -1329,7 +1355,7 @@ static void atomic2gen (lua_State *L, global_State *g) { ** total number of objects grows 'genminormul'%. */ static void setminordebt (global_State *g) { - luaE_setdebt(g, applygcparam(g, genminormul, gettotalobjs(g))); + luaE_setdebt(g, applygcparam(g, genminormul, g->GCmajorminor)); } @@ -1369,13 +1395,11 @@ static void enterinc (global_State *g) { */ void luaC_changemode (lua_State *L, int newmode) { global_State *g = G(L); + if (g->gckind == KGC_GENMAJOR) /* doing major collections? */ + g->gckind = KGC_INC; /* already incremental but in name */ if (newmode != g->gckind) { /* does it need to change? */ - if (newmode == KGC_INC) { /* entering incremental mode? */ - if (g->gckind == KGC_GENMAJOR) - g->gckind = KGC_INC; /* already incremental but in name */ - else - enterinc(g); /* entering incremental mode */ - } + if (newmode == KGC_INC) /* entering incremental mode? */ + enterinc(g); /* entering incremental mode */ else { lua_assert(newmode == KGC_GENMINOR); entergen(L, g); @@ -1396,16 +1420,24 @@ static void fullgen (lua_State *L, global_State *g) { /* ** After an atomic incremental step from a major collection, ** check whether collector could return to minor collections. +** It checks whether the number of objects 'tobecollected' +** is greater than 'majorminor'% of the number of objects added +** since the last collection ('addedobjs'). */ static int checkmajorminor (lua_State *L, global_State *g) { if (g->gckind == KGC_GENMAJOR) { - l_obj numobjs = gettotalobjs(g); /* current count */ - if (g->marked < numobjs - (numobjs >> 2)) { /* ?? */ + l_obj numobjs = gettotalobjs(g); + l_obj addedobjs = numobjs - g->GCmajorminor; + l_obj limit = applygcparam(g, majorminor, addedobjs); + l_obj tobecollected = numobjs - g->marked; +//printf("-> minor? %ld %ld %ld\n", tobecollected, limit, numobjs); + if (tobecollected > limit) { atomic2gen(L, g); /* return to generational mode */ setminordebt(g); return 0; /* exit incremental collection */ } } + g->GCmajorminor = g->marked; /* prepare for next collection */ return 1; /* stay doing incremental collections */ } @@ -1634,8 +1666,6 @@ void luaC_step (lua_State *L) { if (!gcrunning(g)) /* not running? */ luaE_setdebt(g, 2000); else { -//printf("> step: %d %d %ld %ld -> ", g->gckind, g->gcstate, gettotalobjs(g), g->GCdebt); - switch (g->gckind) { case KGC_INC: case KGC_GENMAJOR: incstep(L, g); @@ -1645,7 +1675,6 @@ void luaC_step (lua_State *L) { setminordebt(g); break; } -//printf("%d %d %ld %ld\n", g->gckind, g->gcstate, gettotalobjs(g), g->GCdebt); } } diff --git a/lgc.h b/lgc.h index 3cc0c80d..4cbc6e61 100644 --- a/lgc.h +++ b/lgc.h @@ -161,10 +161,24 @@ /* Default Values for GC parameters */ -/* generational */ +/* +** Minor collections will shift to major ones after LUAI_MINORMAJOR% +** objects become old. +*/ +#define LUAI_MINORMAJOR 100 + +/* +** Major collections will shift to minor ones after a collection +** collects at least LUAI_MAJORMINOR% of the new objects. +*/ +#define LUAI_MAJORMINOR 80 + +/* +** A young (minor) collection will run after creating LUAI_GENMINORMUL% +** new objects. +*/ +#define LUAI_GENMINORMUL 20 -#define LUAI_GENMAJORMUL 100 /* major multiplier */ -#define LUAI_GENMINORMUL 20 /* minor multiplier */ /* incremental */ @@ -187,27 +201,17 @@ /* ** Macros to set and apply GC parameters. GC parameters are given in -** percentage points, but are stored as lu_byte. To reduce their -** values and avoid repeated divisions by 100, these macros store -** the original parameter multiplied by 2^n and divided by 100. -** To apply them, the value is divided by 2^n (a shift) and then -** multiplied by the stored parameter, yielding -** value / 2^n * (original parameter * 2^n / 100), or approximately -** (value * original parameter / 100). -** -** For most parameters, which are typically larger than 100%, 2^n is -** 16 (2^4), allowing maximum values up to ~1500%, with a granularity -** of ~6%. For the minor multiplier, which is typically smaller, -** 2^n is 64 (2^6) to allow more precision. In that case, the maximum -** value is ~400%, with a granularity of ~1.5%. +** percentage points, but are stored as lu_byte. To avoid repeated +** divisions by 100, these macros store the original parameter +** multiplied by 128 and divided by 100. To apply them, if it first +** divides the value by 128 it may lose precision; if it first +** multiplies by the parameter, it may overflow. So, it first divides +** by 32, then multiply by the parameter, and then divides the result by +** 4. */ -#define gcparamshift(p) \ - (offsetof(global_State, p) == offsetof(global_State, genminormul) ? 6 : 4) - -#define setgcparam(g,p,v) \ - (g->p = (cast_uint(v) << gcparamshift(p)) / 100u) -#define applygcparam(g,p,v) (((v) >> gcparamshift(p)) * g->p) +#define setgcparam(g,p,v) (g->gcp##p = (cast_uint(v) << 7) / 100u) +#define applygcparam(g,p,v) ((((v) >> 5) * g->gcp##p) >> 2) /* diff --git a/lstate.c b/lstate.c index 1216db35..aab90e34 100644 --- a/lstate.c +++ b/lstate.c @@ -368,8 +368,9 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud, unsigned int seed) { setgcparam(g, gcpause, LUAI_GCPAUSE); setgcparam(g, gcstepmul, LUAI_GCMUL); g->gcstepsize = LUAI_GCSTEPSIZE; - setgcparam(g, genmajormul, LUAI_GENMAJORMUL); setgcparam(g, genminormul, LUAI_GENMINORMUL); + setgcparam(g, minormajor, LUAI_MINORMAJOR); + setgcparam(g, majorminor, LUAI_MAJORMINOR); for (i=0; i < LUA_NUMTAGS; i++) g->mt[i] = NULL; if (luaD_rawrunprotected(L, f_luaopen, NULL) != LUA_OK) { /* memory allocation error: free partial state */ diff --git a/lstate.h b/lstate.h index 1868981b..49acfb7e 100644 --- a/lstate.h +++ b/lstate.h @@ -264,16 +264,17 @@ typedef struct global_State { TValue l_registry; TValue nilvalue; /* a nil value */ unsigned int seed; /* randomized seed for hashes */ + unsigned short gcpgenminormul; /* control minor generational collections */ + unsigned short gcpmajorminor; /* control shift major->minor */ + unsigned short gcpminormajor; /* control shift minor->major */ + unsigned short gcpgcpause; /* size of pause between successive GCs */ + unsigned short gcpgcstepmul; /* GC "speed" */ lu_byte currentwhite; lu_byte gcstate; /* state of garbage collector */ lu_byte gckind; /* kind of GC running */ lu_byte gcstopem; /* stops emergency collections */ - lu_byte genminormul; /* control for minor generational collections */ - lu_byte genmajormul; /* control for major generational collections */ lu_byte gcstp; /* control whether GC is running */ lu_byte gcemergency; /* true if this is an emergency collection */ - lu_byte gcpause; /* size of pause between successive GCs */ - lu_byte gcstepmul; /* GC "speed" */ lu_byte gcstepsize; /* (log2 of) GC granularity */ GCObject *allgc; /* list of all collectable objects */ GCObject **sweepgc; /* current position of sweep in list */ diff --git a/lua.c b/lua.c index 1e884b07..d26dd8ac 100644 --- a/lua.c +++ b/lua.c @@ -646,7 +646,7 @@ static int pmain (lua_State *L) { luai_openlibs(L); /* open standard libraries */ createargtable(L, argv, argc, script); /* create table 'arg' */ lua_gc(L, LUA_GCRESTART); /* start GC... */ - lua_gc(L, LUA_GCGEN, 0, 0); /* ...in generational mode */ + lua_gc(L, LUA_GCGEN, -1, -1, -1); /* ...in generational mode */ if (!(args & has_E)) { /* no option '-E'? */ if (handle_luainit(L) != LUA_OK) /* run LUA_INIT */ return 0; /* error running LUA_INIT */ diff --git a/manual/manual.of b/manual/manual.of index 263ced72..8607e57d 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -621,7 +621,8 @@ that is inaccessible from Lua. another live object refer to the object.) Because Lua has no knowledge about @N{C code}, it never collects objects accessible through the registry @see{registry}, -which includes the global environment @see{globalenv}. +which includes the global environment @see{globalenv} and +the main thread. The garbage collector (GC) in Lua can work in two modes: @@ -638,8 +639,8 @@ therefore, optimal settings are also non-portable. You can change the GC mode and parameters by calling @Lid{lua_gc} @N{in C} or @Lid{collectgarbage} in Lua. -You can also use these functions to control -the collector directly (e.g., to stop and restart it). +You can also use these functions to control the collector directly, +for instance to stop or restart it. } @@ -656,39 +657,36 @@ and the @def{garbage-collector step size}. The garbage-collector pause controls how long the collector waits before starting a new cycle. -The collector starts a new cycle when the use of memory -hits @M{n%} of the use after the previous collection. +The collector starts a new cycle when the number of objects +hits @M{n%} of the total after the previous collection. Larger values make the collector less aggressive. 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 memory in use -to double before starting 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 garbage-collector step multiplier controls the speed of the collector relative to -memory allocation, +object creation, that is, -how many elements it marks or sweeps for each -kilobyte of memory allocated. -Larger values make the collector more aggressive but also increase -the size of each incremental step. -You should not use values less than 100, -because they make the collector too slow and -can result in the collector never finishing a cycle. -The default value is 100; the maximum value is 1000. +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 garbage-collector step size controls the size of each incremental step, -specifically how many bytes the interpreter allocates +specifically how many objects the interpreter creates before performing a step. This parameter is logarithmic: -A value of @M{n} means the interpreter will allocate @M{2@sp{n}} -bytes between steps and perform equivalent work during the step. +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. -The default value is 13, -which means steps of approximately @N{8 Kbytes}. +The default value is 8, +which means steps of approximately @N{256 objects}. } @@ -697,31 +695,44 @@ which means steps of approximately @N{8 Kbytes}. In generational mode, the collector does frequent @emph{minor} collections, which traverses only objects recently created. -If after a minor collection the use of memory is still above a limit, -the collector does a stop-the-world @emph{major} collection, +If after a minor collection the number of objects is above a limit, +the collector shifts to a @emph{major} collection, which traverses all objects. -The generational mode uses two parameters: -the @def{minor multiplier} and the @def{the major multiplier}. +The collector will then stay doing major collections until +it detects that the program is generating enough garbage to justify +going back to minor collections. + +The generational mode uses three parameters: +the @def{minor multiplier}, the @def{minor-major multiplier}, +and the @def{major-minor multiplier}. The minor multiplier controls the frequency of minor collections. For a minor multiplier @M{x}, -a new minor collection will be done when memory -grows @M{x%} larger than the memory in use after the previous major -collection. +a new minor collection will be done when the number of objects +grows @M{x%} larger than the number in use just after the last collection. For instance, for a multiplier of 20, -the collector will do a minor collection when the use of memory -gets 20% larger than the use after the previous major collection. -The default value is 20; the maximum value is 200. +the collector will do a minor collection when the number of objects +gets 20% larger than the total after the last major collection. +The default value is 20. -The major multiplier controls the frequency of major collections. -For a major multiplier @M{x}, -a new major collection will be done when memory -grows @M{x%} larger than the memory in use after the previous major -collection. +The minor-major multiplier controls the shift to major collections. +For a multiplier @M{x}, +the collector will shift to a major collection +when the number of old objects grows @M{x%} larger +than the total after the previous major collection. For instance, for a multiplier of 100, -the collector will do a major collection when the use of memory -gets larger than twice the use after the previous collection. -The default value is 100; the maximum value is 1000. +the collector will do a major collection when the number of old objects +gets larger than twice the total after the previous major collection. +The default value is 100. + +The major-minor multiplier controls the shift back to minor collections. +For a multiplier @M{x}, +the collector will shift back to minor collections +after a major collection collects at least @M{x%} of the allocated objects. +In particular, for a multiplier of 0, +the collector will immediately shift back to minor collections +after doing one cycle of major collections. +The default value is 20. } @@ -3311,9 +3322,8 @@ Returns the remainder of dividing the current amount of bytes of memory in use by Lua by 1024. } -@item{@id{LUA_GCSTEP} @T{(int stepsize)}| -Performs an incremental step of garbage collection, -corresponding to the allocation of @id{stepsize} Kbytes. +@item{@id{LUA_GCSTEP}| +Performs a step of garbage collection. } @item{@id{LUA_GCISRUNNING}| @@ -3321,13 +3331,13 @@ Returns a boolean that tells whether the collector is running (i.e., not stopped). } -@item{@id{LUA_GCINC} (int pause, int stepmul, stepsize)| +@item{@id{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 majormul)| +@item{@id{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}). @@ -6312,13 +6322,14 @@ gives the exact number of bytes in use by Lua. @item{@St{step}| Performs a garbage-collection step. -The step @Q{size} is controlled by @id{arg}. -With a zero value, -the collector will perform one basic (indivisible) step. -For non-zero values, -the collector will perform as if that amount of memory -(in Kbytes) had been allocated by Lua. -Returns @true if the step finished a collection cycle. +In incremental mode, +that step corresponds to the current step size; +the function returns @true if the step finished a collection cycle. +In generational mode, +the step performs a full minor collection or +a major collection, +if the collector has scheduled one; +the function returns @true if the step performed a major collection. } @item{@St{isrunning}| @@ -6332,15 +6343,15 @@ This option can be followed by three numbers: the garbage-collector pause, the step multiplier, and the step size @see{incmode}. -A zero means to not change that value. +A -1 or absent value means to not change that value. } @item{@St{generational}| Change the collector mode to generational. -This option can be followed by two numbers: -the garbage-collector minor multiplier -and the major multiplier @see{genmode}. -A zero means to not change that value. +This option can be followed by three numbers: +the garbage-collector minor multiplier, +the minor-major multiplier, and the major-minor multiplier @see{genmode}. +A -1 or absent value means to not change that value. } } @@ -9229,6 +9240,9 @@ declare a local variable with the same name in the loop body. @itemize{ @item{ +There were several changes in the parameters +for the options @St{incremental} and @St{generational} +of the function @Lid{collectgarbage}. } } @@ -9245,6 +9259,12 @@ it is equivalent to @Lid{lua_closethread} with @id{from} being @id{NULL}. } +@item{ +There were several changes in the parameters +for the options @Lid{LUA_GCINC} and @Lid{LUA_GCGEN} +of the function @Lid{lua_gc}. +} + } }