Browse Source

generational collection: new attempt (still incomplete)

Roberto Ierusalimschy 8 years ago
parent
commit
f5f3df3bd1
8 changed files with 195 additions and 40 deletions
  1. 9 1
      lapi.c
  2. 3 3
      lbaselib.c
  3. 159 24
      lgc.c
  4. 6 3
      lgc.h
  5. 2 1
      lstate.c
  6. 6 2
      lstate.h
  7. 7 5
      ltests.c
  8. 3 1
      lua.h

+ 9 - 1
lapi.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lapi.c,v 2.258 2016/01/05 16:07:21 roberto Exp roberto $
+** $Id: lapi.c,v 2.259 2016/02/29 14:27:14 roberto Exp roberto $
 ** Lua API
 ** See Copyright Notice in lua.h
 */
@@ -1097,6 +1097,14 @@ LUA_API int lua_gc (lua_State *L, int what, int data) {
       res = g->gcrunning;
       break;
     }
+    case LUA_GCGEN: {
+      luaC_changemode(L, KGC_GEN);
+      break;
+    }
+    case LUA_GCINC: {
+      luaC_changemode(L, KGC_NORMAL);
+      break;
+    }
     default: res = -1;  /* invalid option */
   }
   lua_unlock(L);

+ 3 - 3
lbaselib.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lbaselib.c,v 1.313 2016/04/11 19:18:40 roberto Exp roberto $
+** $Id: lbaselib.c,v 1.314 2016/09/05 19:06:34 roberto Exp roberto $
 ** Basic library
 ** See Copyright Notice in lua.h
 */
@@ -173,10 +173,10 @@ static int luaB_rawset (lua_State *L) {
 static int luaB_collectgarbage (lua_State *L) {
   static const char *const opts[] = {"stop", "restart", "collect",
     "count", "step", "setpause", "setstepmul",
-    "isrunning", NULL};
+    "isrunning", "generational", "incremental", NULL};
   static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT,
     LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL,
-    LUA_GCISRUNNING};
+    LUA_GCISRUNNING, LUA_GCGEN, LUA_GCINC};
   int o = optsnum[luaL_checkoption(L, 1, "collect", opts)];
   int ex = (int)luaL_optinteger(L, 2, 0);
   int res = lua_gc(L, o, ex);

+ 159 - 24
lgc.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.c,v 2.214 2016/11/07 12:38:35 roberto Exp roberto $
+** $Id: lgc.c,v 2.215 2016/12/22 13:08:50 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -59,11 +59,10 @@
 #define PAUSEADJ		100
 
 
-/*
-** 'makewhite' erases all color bits then sets only the current white
-** bit
-*/
+/* mask to erase all color bits */
 #define maskcolors	(~(bitmask(BLACKBIT) | WHITEBITS))
+
+/* macro to erase all color bits then sets only the current white bit */
 #define makewhite(g,x)	\
  (x->marked = cast_byte((x->marked & maskcolors) | luaC_white(g)))
 
@@ -92,6 +91,7 @@
 #define markobjectN(g,t)	{ if (t) markobject(g,t); }
 
 static void reallymarkobject (global_State *g, GCObject *o);
+static l_mem atomic (lua_State *L);
 
 
 /*
@@ -155,8 +155,11 @@ static int iscleared (global_State *g, const TValue *o) {
 void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v) {
   global_State *g = G(L);
   lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
-  if (keepinvariant(g))  /* must keep invariant? */
+  if (keepinvariant(g)) {  /* must keep invariant? */
     reallymarkobject(g, v);  /* restore invariant */
+    if (isold(o))
+      l_setbit((v)->marked, OLDBIT);
+  }
   else {  /* sweep phase */
     lua_assert(issweepphase(g));
     makewhite(g, o);  /* mark main obj. as white to avoid other barriers */
@@ -186,8 +189,10 @@ void luaC_upvalbarrier_ (lua_State *L, UpVal *uv) {
   global_State *g = G(L);
   GCObject *o = gcvalue(uv->v);
   lua_assert(!upisopen(uv));  /* ensured by macro luaC_upvalbarrier */
-  if (keepinvariant(g))
+  if (keepinvariant(g)) {
     markobject(g, o);
+    l_setbit((o)->marked, OLDBIT);
+  }
 }
 
 
@@ -922,6 +927,121 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
 /* }====================================================== */
 
 
+/*
+** {======================================================
+** Generational Collector
+** =======================================================
+*/
+
+
+#if 0
+static int count (GCObject *p, GCObject *limit) {
+  int res = 0;
+  for (; p != NULL && p != limit; p = p->next)
+    res++;
+  return res;
+}
+#endif
+
+
+static GCObject **sweepgen (lua_State *L, GCObject **p, GCObject *limit,
+                            int zeromask, int onemask) {
+  global_State *g = G(L);
+  int ow = otherwhite(g);
+  GCObject *curr;
+  while ((curr = *p) != limit) {
+    int marked = curr->marked;
+    if (isdeadm(ow, marked)) {  /* is 'curr' dead? */
+      lua_assert(!isold(curr));
+      *p = curr->next;  /* remove 'curr' from list */
+      freeobj(L, curr);  /* erase 'curr' */
+    }
+    else {  /* correct mark */
+      if (!isold(curr))  /* don't change old objects */
+        curr->marked = cast_byte((marked & zeromask) | onemask);
+      p = &curr->next;  /* go to next element */
+    }
+  }
+  return p;
+}
+
+
+static void startgencycle (lua_State *L, global_State *g) {
+  propagateall(g);
+  atomic(L);
+}
+
+
+static void finishgencycle (lua_State *L, global_State *g, int mask) {
+  sweepgen(L, &g->finobj, NULL, ~0, mask);
+  sweepgen(L, &g->tobefnz, NULL, ~0, mask);
+  checkSizes(L, g);
+  g->gcstate = GCSpropagate;  /* skip restart */
+  callallpendingfinalizers(L);
+}
+
+
+static void youngcollection (lua_State *L, global_State *g) {
+  GCObject **psurvival;
+  lua_assert(g->gcstate == GCSpropagate);
+  startgencycle(L, g);
+  /* sweep nursery */
+  psurvival = sweepgen(L, &g->allgc, g->survival, maskcolors, luaC_white(g));
+  lua_assert(*psurvival == g->survival);
+  /* sweep 'survival' list, making elements old */
+  sweepgen(L, psurvival, g->old, ~0, bitmask(OLDBIT));
+  /* incorporate 'survival' list into old list */
+  g->old = *psurvival;
+  /* surviving young objects go to 'survival' list */
+  g->survival = g->allgc;
+  finishgencycle(L, g, 0);
+lua_checkmemory(L);
+}
+
+
+static void entergen (lua_State *L, global_State *g) {
+lua_checkmemory(L);
+  lua_assert(g->old == NULL && g->survival == NULL);
+  luaC_runtilstate(L, bitmask(GCSpause));  /* prepare to start a new cycle */
+  luaC_runtilstate(L, bitmask(GCSpropagate));  /* start new cycle */
+  startgencycle(L, g);
+  /* sweep all ellements making them old */
+  sweepgen(L, &g->allgc, g->survival, ~0, bitmask(OLDBIT));
+  /* everything alive now is old; 'survival' is empty */
+  g->old = g->survival = g->allgc;
+  finishgencycle(L, g, bitmask(OLDBIT));
+lua_checkmemory(L);
+}
+
+
+void luaC_changemode (lua_State *L, int newmode) {
+  global_State *g = G(L);
+  if (newmode != g->gckind) {  /* otherwise, nothing to be done */
+    if (newmode == KGC_GEN)  /* entering generational mode? */
+      entergen(L, g);
+    else {  /* entering incremental mode */
+lua_checkmemory(L);
+      youngcollection(L, g);
+      g->old = g->survival = NULL;
+lua_checkmemory(L);
+    }
+    g->gckind = newmode;
+  }
+}
+
+
+static void genstep (lua_State *L, global_State *g) {
+  lu_mem mem;
+  youngcollection(L, g);
+  mem = gettotalbytes(g);
+  luaE_setdebt(g, -((mem / 100) * 20));
+}
+
+
+
+
+/* }====================================================== */
+
 
 /*
 ** {======================================================
@@ -963,17 +1083,24 @@ static void entersweep (lua_State *L) {
 }
 
 
+static void deletealllist (lua_State *L, GCObject *p) {
+  while (p) {
+    GCObject *next = p->next;
+    freeobj(L, p);
+    p = next;
+  }
+}
+
+
 void luaC_freeallobjects (lua_State *L) {
   global_State *g = G(L);
   separatetobefnz(g, 1);  /* separate all objects with finalizers */
   lua_assert(g->finobj == NULL);
   callallpendingfinalizers(L);
   lua_assert(g->tobefnz == NULL);
-  g->currentwhite = WHITEBITS; /* this "white" makes all objects look dead */
-  g->gckind = KGC_NORMAL;
-  sweepwholelist(L, &g->finobj);
-  sweepwholelist(L, &g->allgc);
-  sweepwholelist(L, &g->fixedgc);  /* collect fixed objects */
+  deletealllist(L, g->finobj);
+  deletealllist(L, g->allgc);
+  deletealllist(L, g->fixedgc);  /* collect fixed objects */
   lua_assert(g->strt.nuse == 0);
 }
 
@@ -996,6 +1123,7 @@ static l_mem atomic (lua_State *L) {
   propagateall(g);  /* propagate changes */
   work = g->GCmemtrav;  /* stop counting (do not recount 'grayagain') */
   g->gray = grayagain;
+  g->grayagain = NULL;
   propagateall(g);  /* traverse 'grayagain' list */
   g->GCmemtrav = 0;  /* restart counting */
   convergeephemerons(g);
@@ -1052,10 +1180,10 @@ static lu_mem singlestep (lua_State *L) {
     }
     case GCSpropagate: {
       g->GCmemtrav = 0;
-      lua_assert(g->gray);
-      propagatemark(g);
-       if (g->gray == NULL)  /* no more gray objects? */
+      if (g->gray == NULL)  /* no more gray objects? */
         g->gcstate = GCSatomic;  /* finish propagate phase */
+      else
+        propagatemark(g);  /* traverse one gray object */
       return g->GCmemtrav;  /* memory traversed in this step */
     }
     case GCSatomic: {
@@ -1123,15 +1251,10 @@ static l_mem getdebt (global_State *g) {
 }
 
 /*
-** performs a basic GC step when collector is running
+** performs a basic incremental step
 */
-void luaC_step (lua_State *L) {
-  global_State *g = G(L);
-  l_mem debt = getdebt(g);  /* GC deficit (be paid now) */
-  if (!g->gcrunning) {  /* not running? */
-    luaE_setdebt(g, -GCSTEPSIZE * 10);  /* avoid being called too often */
-    return;
-  }
+static void incstep (lua_State *L, global_State *g) {
+  l_mem debt = getdebt(g);  /* GC deficit (to be paid now) */
   do {  /* repeat until pause or enough "credit" (negative debt) */
     lu_mem work = singlestep(L);  /* perform one single step */
     debt -= work;
@@ -1145,6 +1268,19 @@ void luaC_step (lua_State *L) {
   }
 }
 
+/*
+** performs a basic GC step when collector is running
+*/
+void luaC_step (lua_State *L) {
+  global_State *g = G(L);
+  if (!g->gcrunning)  /* not running? */
+    luaE_setdebt(g, -GCSTEPSIZE * 10);  /* avoid being called too often */
+  else if (g->gckind == KGC_NORMAL)
+    incstep(L, g);
+  else
+    genstep(L, g);
+}
+
 
 /*
 ** Performs a full GC cycle; if 'isemergency', set a flag to avoid
@@ -1164,7 +1300,6 @@ void luaC_fullgc (lua_State *L, int isemergency) {
   }
   /* finish any pending sweep phase to start a new cycle */
   luaC_runtilstate(L, bitmask(GCSpause));
-  luaC_runtilstate(L, ~bitmask(GCSpause));  /* start new collection */
   luaC_runtilstate(L, bitmask(GCScallfin));  /* run up to finalizers */
   /* estimate must be correct after a full GC cycle */
   lua_assert(g->GCestimate == gettotalbytes(g));

+ 6 - 3
lgc.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lgc.h,v 2.90 2015/10/21 18:15:15 roberto Exp roberto $
+** $Id: lgc.h,v 2.91 2015/12/21 13:02:14 roberto Exp roberto $
 ** Garbage Collector
 ** See Copyright Notice in lua.h
 */
@@ -79,7 +79,8 @@
 #define WHITE1BIT	1  /* object is white (type 1) */
 #define BLACKBIT	2  /* object is black */
 #define FINALIZEDBIT	3  /* object has been marked for finalization */
-/* bit 7 is currently used by tests (luaL_checkmemory) */
+#define OLDBIT		4  /* object is old (gen. mode) */
+#define TESTGRAYBIT	7  /* used by tests (luaL_checkmemory) */
 
 #define WHITEBITS	bit2mask(WHITE0BIT, WHITE1BIT)
 
@@ -88,11 +89,12 @@
 #define isblack(x)      testbit((x)->marked, BLACKBIT)
 #define isgray(x)  /* neither white nor black */  \
 	(!testbits((x)->marked, WHITEBITS | bitmask(BLACKBIT)))
+#define isold(x)	testbit((x)->marked, OLDBIT)
 
 #define tofinalize(x)	testbit((x)->marked, FINALIZEDBIT)
 
 #define otherwhite(g)	((g)->currentwhite ^ WHITEBITS)
-#define isdeadm(ow,m)	(!(((m) ^ WHITEBITS) & (ow)))
+#define isdeadm(ow,m)	((m) & (ow))
 #define isdead(g,v)	isdeadm(otherwhite(g), (v)->marked)
 
 #define changewhite(x)	((x)->marked ^= WHITEBITS)
@@ -142,6 +144,7 @@ LUAI_FUNC void luaC_barrierback_ (lua_State *L, Table *o);
 LUAI_FUNC void luaC_upvalbarrier_ (lua_State *L, UpVal *uv);
 LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
 LUAI_FUNC void luaC_upvdeccount (lua_State *L, UpVal *uv);
+LUAI_FUNC void luaC_changemode (lua_State *L, int newmode);
 
 
 #endif

+ 2 - 1
lstate.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.c,v 2.132 2015/11/02 16:01:41 roberto Exp roberto $
+** $Id: lstate.c,v 2.133 2015/11/13 12:16:51 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -319,6 +319,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
   g->gcstate = GCSpause;
   g->gckind = KGC_NORMAL;
   g->allgc = g->finobj = g->tobefnz = g->fixedgc = NULL;
+  g->survival = g->old = NULL;
   g->sweepgc = NULL;
   g->gray = g->grayagain = NULL;
   g->weak = g->ephemeron = g->allweak = NULL;

+ 6 - 2
lstate.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstate.h,v 2.133 2016/12/22 13:08:50 roberto Exp roberto $
+** $Id: lstate.h,v 2.134 2017/02/15 18:52:13 roberto Exp roberto $
 ** Global State
 ** See Copyright Notice in lua.h
 */
@@ -70,7 +70,8 @@ struct lua_longjmp;  /* defined in ldo.c */
 
 /* kinds of Garbage Collection */
 #define KGC_NORMAL	0
-#define KGC_EMERGENCY	1	/* gc was forced by an allocation failure */
+#define KGC_GEN		1	/* generational gc */
+#define KGC_EMERGENCY	2	/* gc was forced by an allocation failure */
 
 
 typedef struct stringtable {
@@ -158,6 +159,9 @@ typedef struct global_State {
   GCObject *allweak;  /* list of all-weak tables */
   GCObject *tobefnz;  /* list of userdata to be GC */
   GCObject *fixedgc;  /* list of objects not to be collected */
+  /* fields for generational collector */
+  GCObject *old;  /* start of old objects */
+  GCObject *survival;  /* start of objects that survived one GC cycle */
   struct lua_State *twups;  /* list of threads with open upvalues */
   unsigned int gcfinnum;  /* number of finalizers to call in each GC step */
   int gcpause;  /* size of pause between successive GCs */

+ 7 - 5
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 2.210 2016/11/07 12:38:35 roberto Exp roberto $
+** $Id: ltests.c,v 2.211 2016/12/04 20:17:24 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -195,9 +195,13 @@ static int testobjref1 (global_State *g, GCObject *f, GCObject *t) {
 
 
 static void printobj (global_State *g, GCObject *o) {
-  printf("||%s(%p)-%c(%02X)||",
+  printf("||%s(%p)-%c%c(%02X)||",
            ttypename(novariant(o->tt)), (void *)o,
-           isdead(g,o)?'d':isblack(o)?'b':iswhite(o)?'w':'g', o->marked);
+           isdead(g,o) ? 'd' : isblack(o) ? 'b' : iswhite(o) ? 'w' : 'g',
+           testbit(o->marked, OLDBIT) ? 'o' : 'n',
+           o->marked);
+  if (o->tt == LUA_TSHRSTR || o->tt == LUA_TLNGSTR)
+    printf(" '%s'", getstr(gco2ts(o)));
 }
 
 
@@ -364,8 +368,6 @@ static void checkobject (global_State *g, GCObject *o, int maybedead) {
 }
 
 
-#define TESTGRAYBIT		7
-
 static void checkgraylist (global_State *g, GCObject *o) {
   ((void)g);  /* better to keep it available if we need to print an object */
   while (o) {

+ 3 - 1
lua.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lua.h,v 1.331 2016/05/30 15:53:28 roberto Exp roberto $
+** $Id: lua.h,v 1.332 2016/12/22 15:51:20 roberto Exp roberto $
 ** Lua - A Scripting Language
 ** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
 ** See Copyright Notice at the end of this file
@@ -308,6 +308,8 @@ LUA_API int (lua_isyieldable) (lua_State *L);
 #define LUA_GCSETPAUSE		6
 #define LUA_GCSETSTEPMUL	7
 #define LUA_GCISRUNNING		9
+#define LUA_GCGEN		10
+#define LUA_GCINC		11
 
 LUA_API int (lua_gc) (lua_State *L, int what, int data);