From 0b062414831e3794fcdb747e53e9662d112473cf Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Mon, 30 Aug 2004 10:44:44 -0300 Subject: [PATCH] better control for GC cycles --- lapi.c | 10 +++---- lgc.c | 84 ++++++++++++++++++++++++++++++++++---------------------- lgc.h | 13 +++++---- lmem.c | 5 ++-- lstate.c | 14 ++++++---- lstate.h | 6 ++-- 6 files changed, 77 insertions(+), 55 deletions(-) diff --git a/lapi.c b/lapi.c index 83fe3f40..c8cf819b 100644 --- a/lapi.c +++ b/lapi.c @@ -1,5 +1,5 @@ /* -** $Id: lapi.c,v 2.16 2004/08/12 17:02:51 roberto Exp roberto $ +** $Id: lapi.c,v 2.17 2004/08/17 17:45:45 roberto Exp roberto $ ** Lua API ** See Copyright Notice in lua.h */ @@ -831,7 +831,7 @@ LUA_API int lua_gc (lua_State *L, int what, int data) { break; } case LUA_GCRESTART: { - g->GCthreshold = g->nblocks; + g->GCthreshold = g->totalbytes; break; } case LUA_GCCOLLECT: { @@ -840,13 +840,13 @@ LUA_API int lua_gc (lua_State *L, int what, int data) { } case LUA_GCCOUNT: { /* GC values are expressed in Kbytes: #bytes/2^10 */ - res = cast(int, g->nblocks >> 10); + res = cast(int, g->totalbytes >> 10); break; } case LUA_GCSTEP: { lu_mem a = (cast(lu_mem, data) << 10); - if (a <= g->nblocks) - g->GCthreshold = g->nblocks - a; + if (a <= g->totalbytes) + g->GCthreshold = g->totalbytes - a; else g->GCthreshold = 0; luaC_step(L); diff --git a/lgc.c b/lgc.c index 561c6350..94d801ab 100644 --- a/lgc.c +++ b/lgc.c @@ -1,5 +1,5 @@ /* -** $Id: lgc.c,v 2.8 2004/08/10 19:17:23 roberto Exp roberto $ +** $Id: lgc.c,v 2.9 2004/08/24 20:12:06 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -23,13 +23,11 @@ #include "ltm.h" -#define GCSTEPSIZE (40*sizeof(TValue)) +#define GCSTEPSIZE 1000 #define STEPMUL 2 -#define GCSWEEPMAX 40 -#define GCSWEEPCOST 1 -#define GCFINALIZECOST (10*sizeof(TValue)) -#define WAITNEXTCYCLE (40 * GCSTEPSIZE) -#define WAITNEXTCYCLEGN (200 * GCSTEPSIZE) +#define GCSWEEPMAX 10 +#define GCSWEEPCOST 30 +#define GCFINALIZECOST 100 #define FIXEDMASK bitmask(FIXEDBIT) @@ -408,10 +406,11 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_int32 count) { global_State *g = G(L); int whitebit = otherwhite(g); int deadmask = whitebit | FIXEDMASK; - while ((curr = *p) != NULL) { + int generational = g->gcgenerational; + while ((curr = *p) != NULL && count-- > 0) { if ((curr->gch.marked ^ whitebit) & deadmask) { lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); - if (!G(L)->gcgenerational || isdead(g, curr)) + if (!generational || isdead(g, curr)) makewhite(g, curr); if (curr->gch.tt == LUA_TTHREAD) sweepwholelist(L, &gco2th(curr)->openupval); @@ -424,17 +423,11 @@ static GCObject **sweeplist (lua_State *L, GCObject **p, lu_int32 count) { g->rootgc = curr->gch.next; /* adjust first */ freeobj(L, curr); } - if (count-- == 0) break; } return p; } -static void sweepstrings (lua_State *L) { - global_State *g = G(L); - sweepwholelist(L, &G(L)->strt.hash[g->sweepstrgc++]); -} - static void freelist (lua_State *L, GCObject **p) { while (*p) { @@ -532,6 +525,7 @@ static void remarkupvals (global_State *g) { static void atomic (lua_State *L) { global_State *g = G(L); + int aux; lua_assert(g->gray == NULL); /* remark occasional upvalues of (maybe) dead threads */ remarkupvals(g); @@ -554,7 +548,11 @@ static void atomic (lua_State *L) { g->sweepstrgc = 0; g->sweepgc = &g->rootgc; g->gcstate = GCSsweepstring; - if (g->gcgenerational++ > 20) g->gcgenerational = 0; + aux = g->gcgenerational; + g->gcgenerational = (g->estimate <= 4*g->prevestimate/2); + if (!aux) /* last collection was full? */ + g->prevestimate = g->estimate; /* keep estimate of last full collection */ + g->estimate = g->totalbytes; /* first estimate */ } @@ -562,6 +560,14 @@ static l_mem singlestep (lua_State *L) { global_State *g = G(L); /*lua_checkmemory(L);*/ switch (g->gcstate) { + case GCSpause: { + /* start a new collection */ + if (g->gcgenerational) + atomic(L); + else + markroot(L); + return 0; + } case GCSpropagate: { if (g->gray) return propagatemark(g); @@ -571,33 +577,31 @@ static l_mem singlestep (lua_State *L) { } } case GCSsweepstring: { - sweepstrings(L); + lu_mem old = g->totalbytes; + sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ g->gcstate = GCSsweep; /* end sweep-string phase */ + g->estimate -= old - g->totalbytes; return GCSWEEPCOST; } case GCSsweep: { + lu_mem old = g->totalbytes; g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); if (*g->sweepgc == NULL) { /* nothing more to sweep? */ checkSizes(L); g->gcstate = GCSfinalize; /* end sweep phase */ } - return GCSWEEPCOST; + g->estimate -= old - g->totalbytes; + return GCSWEEPMAX*GCSWEEPCOST; } case GCSfinalize: { if (g->tmudata) { GCTM(L); return GCFINALIZECOST; } - else { /* no more `udata' to finalize */ - if (g->gcgenerational) { - atomic(L); - return WAITNEXTCYCLEGN; - } - else { - markroot(L); /* may restart collection */ - return WAITNEXTCYCLE; - } + else { + g->gcstate = GCSpause; /* end collection */ + return 0; } } default: lua_assert(0); return 0; @@ -607,18 +611,30 @@ static l_mem singlestep (lua_State *L) { void luaC_step (lua_State *L) { global_State *g = G(L); - l_mem lim = (g->nblocks - (g->GCthreshold - GCSTEPSIZE)) * STEPMUL; + l_mem lim = (g->totalbytes - (g->GCthreshold - GCSTEPSIZE)) * STEPMUL; +/*printf("step(%c): ", g->gcgenerational?'g':' ');*/ do { + /*printf("%c", "_pswf"[g->gcstate]);*/ lim -= singlestep(L); + if (g->gcstate == GCSpause) + break; } while (lim > 0); - g->GCthreshold = g->nblocks + GCSTEPSIZE - lim/STEPMUL; - lua_assert((long)g->nblocks + (long)GCSTEPSIZE >= lim/STEPMUL); +/*printf("\n");*/ + if (g->gcstate != GCSpause) + g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/STEPMUL; */ + else { +/*printf("---\n");*/ + lua_assert(g->totalbytes >= g->estimate); + g->GCthreshold = 2*g->estimate; + if (g->GCthreshold < g->totalbytes + GCSTEPSIZE) + g->GCthreshold = g->totalbytes + GCSTEPSIZE; + } } void luaC_fullgc (lua_State *L) { global_State *g = G(L); - if (g->gcstate == GCSpropagate || g->gcgenerational) { + if (g->gcstate <= GCSpropagate || g->gcgenerational) { g->gcgenerational = 0; /* reset sweep marks to sweep all elements (returning them to white) */ g->sweepstrgc = 0; @@ -631,6 +647,7 @@ void luaC_fullgc (lua_State *L) { } /* finish any pending sweep phase */ while (g->gcstate != GCSfinalize) { + lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); singlestep(L); } markroot(L); @@ -639,7 +656,8 @@ void luaC_fullgc (lua_State *L) { singlestep(L); g->gcgenerational = 0; /* keep it in this mode */ } - g->GCthreshold = g->nblocks + GCSTEPSIZE; + lua_assert(g->estimate == g->totalbytes); + g->GCthreshold = 2*g->estimate; luaC_callGCTM(L); /* call finalizers */ } @@ -686,7 +704,7 @@ void luaC_linkupval (lua_State *L, UpVal *uv) { } else { /* sweep phase: sweep it (turning it into white) */ makewhite(g, o); - lua_assert(g->gcstate != GCSfinalize); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); } } } diff --git a/lgc.h b/lgc.h index c1d102f9..b4097b05 100644 --- a/lgc.h +++ b/lgc.h @@ -1,5 +1,5 @@ /* -** $Id: lgc.h,v 2.6 2004/08/10 19:17:23 roberto Exp roberto $ +** $Id: lgc.h,v 2.7 2004/08/24 20:12:06 roberto Exp roberto $ ** Garbage Collector ** See Copyright Notice in lua.h */ @@ -14,10 +14,11 @@ /* ** Possible states of the Garbage Collector */ -#define GCSpropagate 0 -#define GCSsweepstring 1 -#define GCSsweep 2 -#define GCSfinalize 3 +#define GCSpause 0 +#define GCSpropagate 1 +#define GCSsweepstring 2 +#define GCSsweep 3 +#define GCSfinalize 4 /* @@ -71,7 +72,7 @@ #define luaC_white(g) cast(lu_byte, (g)->currentwhite) -#define luaC_checkGC(L) { if (G(L)->nblocks >= G(L)->GCthreshold) \ +#define luaC_checkGC(L) { if (G(L)->totalbytes >= G(L)->GCthreshold) \ luaC_step(L); } diff --git a/lmem.c b/lmem.c index 538951f9..5873e8dc 100644 --- a/lmem.c +++ b/lmem.c @@ -1,5 +1,5 @@ /* -** $Id: lmem.c,v 1.63 2003/11/27 18:18:37 roberto Exp roberto $ +** $Id: lmem.c,v 1.64 2004/04/30 20:13:38 roberto Exp roberto $ ** Interface to Memory Manager ** See Copyright Notice in lua.h */ @@ -77,8 +77,7 @@ void *luaM_realloc (lua_State *L, void *block, lu_mem osize, lu_mem nsize) { if (block == NULL && nsize > 0) luaD_throw(L, LUA_ERRMEM); lua_assert((nsize == 0) == (block == NULL)); - g->nblocks -= osize; - g->nblocks += nsize; + g->totalbytes = (g->totalbytes - osize) + nsize; return block; } diff --git a/lstate.c b/lstate.c index 11f57728..568d8e72 100644 --- a/lstate.c +++ b/lstate.c @@ -1,5 +1,5 @@ /* -** $Id: lstate.c,v 2.10 2004/06/17 14:25:31 roberto Exp roberto $ +** $Id: lstate.c,v 2.11 2004/08/24 20:12:06 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -77,11 +77,12 @@ static void freestack (lua_State *L, lua_State *L1) { */ static void f_luaopen (lua_State *L, void *ud) { Udata *u; /* head of udata list */ + global_State *g = G(L); UNUSED(ud); u = cast(Udata *, luaM_malloc(L, sizeudata(0))); u->uv.len = 0; u->uv.metatable = NULL; - G(L)->firstudata = obj2gco(u); + g->firstudata = obj2gco(u); luaC_link(L, obj2gco(u), LUA_TUSERDATA); setbit(u->uv.marked, FIXEDBIT); setbit(L->marked, FIXEDBIT); @@ -93,7 +94,8 @@ static void f_luaopen (lua_State *L, void *ud) { luaT_init(L); luaX_init(L); luaS_fix(luaS_newliteral(L, MEMERRMSG)); - G(L)->GCthreshold = 4*G(L)->nblocks; + g->GCthreshold = 4*g->totalbytes; + g->prevestimate = g->estimate = g->totalbytes; } @@ -128,7 +130,7 @@ static void close_state (lua_State *L) { luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); luaZ_freebuffer(L, &g->buff); freestack(L, L); - lua_assert(g->nblocks == sizeof(LG)); + lua_assert(g->totalbytes == sizeof(LG)); (*g->realloc)(g->ud, fromstate(L), state_size(LG), 0); } @@ -177,7 +179,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { setnilvalue(registry(L)); luaZ_initbuffer(L, &g->buff); g->panic = NULL; - g->gcstate = GCSfinalize; + g->gcstate = GCSpause; g->gcgenerational = 0; g->rootgc = obj2gco(L); g->sweepstrgc = 0; @@ -190,7 +192,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { setnilvalue(gkey(g->dummynode)); setnilvalue(gval(g->dummynode)); g->dummynode->next = NULL; - g->nblocks = sizeof(LG); + g->totalbytes = sizeof(LG); if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { /* memory allocation error: free partial state */ close_state(L); diff --git a/lstate.h b/lstate.h index 8c85b6f6..d8bda576 100644 --- a/lstate.h +++ b/lstate.h @@ -1,5 +1,5 @@ /* -** $Id: lstate.h,v 2.5 2004/06/02 19:07:55 roberto Exp roberto $ +** $Id: lstate.h,v 2.6 2004/08/24 20:12:06 roberto Exp roberto $ ** Global State ** See Copyright Notice in lua.h */ @@ -82,7 +82,9 @@ typedef struct global_State { GCObject *tmudata; /* list of userdata to be GC */ Mbuffer buff; /* temporary buffer for string concatentation */ lu_mem GCthreshold; - lu_mem nblocks; /* number of `bytes' currently allocated */ + lu_mem totalbytes; /* number of bytes currently allocated */ + lu_mem estimate; /* an estimate of number of bytes actually in use */ + lu_mem prevestimate; /* previous estimate */ lua_CFunction panic; /* to be called in unprotected errors */ TValue _registry; struct lua_State *mainthread;