aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-29 17:05:47 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2020-07-29 17:05:47 -0300
commit0dc5deca1c0182a4a3db2fcfd7bc721f27fb352b (patch)
treeb7af445c2b08aa760b3315c6083c4bb30387f2ee
parentb4c353434f28f3e9d4c45e61d42b4fd07d76cad2 (diff)
downloadlua-0dc5deca1c0182a4a3db2fcfd7bc721f27fb352b.tar.gz
lua-0dc5deca1c0182a4a3db2fcfd7bc721f27fb352b.tar.bz2
lua-0dc5deca1c0182a4a3db2fcfd7bc721f27fb352b.zip
Optimization in 'markold'
OLD1 objects can be potentially anywhere in the 'allgc' list (up to 'reallyold'), but frequently they are all after 'old1' (natural evolution of survivals) or do not exist at all (when all objects die young). So, instead of 'markold' starts looking for them always from the start of 'allgc', the collector keeps an extra pointer, 'firstold1', that points to the first OLD1 object in the 'allgc' list, or is NULL if there are no OLD1 objects in that list.
-rw-r--r--lgc.c59
-rw-r--r--lstate.c2
-rw-r--r--lstate.h18
-rw-r--r--testes/gengc.lua16
4 files changed, 76 insertions, 19 deletions
diff --git a/lgc.c b/lgc.c
index 347b6778..9973c9db 100644
--- a/lgc.c
+++ b/lgc.c
@@ -859,6 +859,8 @@ static GCObject *udata2finalize (global_State *g) {
859 resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */ 859 resetbit(o->marked, FINALIZEDBIT); /* object is "normal" again */
860 if (issweepphase(g)) 860 if (issweepphase(g))
861 makewhite(g, o); /* "sweep" object */ 861 makewhite(g, o); /* "sweep" object */
862 else if (getage(o) == G_OLD1)
863 g->firstold1 = o; /* it is the first OLD1 object in the list */
862 return o; 864 return o;
863} 865}
864 866
@@ -957,6 +959,27 @@ static void separatetobefnz (global_State *g, int all) {
957 959
958 960
959/* 961/*
962** If pointer 'p' points to 'o', move it to the next element.
963*/
964static void checkpointer (GCObject **p, GCObject *o) {
965 if (o == *p)
966 *p = o->next;
967}
968
969
970/*
971** Correct pointers to objects inside 'allgc' list when
972** object 'o' is being removed from the list.
973*/
974static void correctpointers (global_State *g, GCObject *o) {
975 checkpointer(&g->survival, o);
976 checkpointer(&g->old1, o);
977 checkpointer(&g->reallyold, o);
978 checkpointer(&g->firstold1, o);
979}
980
981
982/*
960** if object 'o' has a finalizer, remove it from 'allgc' list (must 983** if object 'o' has a finalizer, remove it from 'allgc' list (must
961** search the list to find it) and link it in 'finobj' list. 984** search the list to find it) and link it in 'finobj' list.
962*/ 985*/
@@ -972,14 +995,8 @@ void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt) {
972 if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */ 995 if (g->sweepgc == &o->next) /* should not remove 'sweepgc' object */
973 g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */ 996 g->sweepgc = sweeptolive(L, g->sweepgc); /* change 'sweepgc' */
974 } 997 }
975 else { /* correct pointers into 'allgc' list, if needed */ 998 else
976 if (o == g->survival) 999 correctpointers(g, o);
977 g->survival = o->next;
978 if (o == g->old1)
979 g->old1 = o->next;
980 if (o == g->reallyold)
981 g->reallyold = o->next;
982 }
983 /* search for pointer pointing to 'o' */ 1000 /* search for pointer pointing to 'o' */
984 for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ } 1001 for (p = &g->allgc; *p != o; p = &(*p)->next) { /* empty */ }
985 *p = o->next; /* remove 'o' from 'allgc' list */ 1002 *p = o->next; /* remove 'o' from 'allgc' list */
@@ -1033,7 +1050,7 @@ static void sweep2old (lua_State *L, GCObject **p) {
1033** will also remove objects turned white here from any gray list. 1050** will also remove objects turned white here from any gray list.
1034*/ 1051*/
1035static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p, 1052static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
1036 GCObject *limit) { 1053 GCObject *limit, GCObject **pfirstold1) {
1037 static const lu_byte nextage[] = { 1054 static const lu_byte nextage[] = {
1038 G_SURVIVAL, /* from G_NEW */ 1055 G_SURVIVAL, /* from G_NEW */
1039 G_OLD1, /* from G_SURVIVAL */ 1056 G_OLD1, /* from G_SURVIVAL */
@@ -1056,8 +1073,11 @@ static GCObject **sweepgen (lua_State *L, global_State *g, GCObject **p,
1056 int marked = curr->marked & maskgcbits; /* erase GC bits */ 1073 int marked = curr->marked & maskgcbits; /* erase GC bits */
1057 curr->marked = cast_byte(marked | G_SURVIVAL | white); 1074 curr->marked = cast_byte(marked | G_SURVIVAL | white);
1058 } 1075 }
1059 else /* all other objects will be old, and so keep their color */ 1076 else { /* all other objects will be old, and so keep their color */
1060 setage(curr, nextage[getage(curr)]); 1077 setage(curr, nextage[getage(curr)]);
1078 if (getage(curr) == G_OLD1 && *pfirstold1 == NULL)
1079 *pfirstold1 = curr; /* first OLD1 object in the list */
1080 }
1061 p = &curr->next; /* go to next element */ 1081 p = &curr->next; /* go to next element */
1062 } 1082 }
1063 } 1083 }
@@ -1169,30 +1189,34 @@ static void finishgencycle (lua_State *L, global_State *g) {
1169*/ 1189*/
1170static void youngcollection (lua_State *L, global_State *g) { 1190static void youngcollection (lua_State *L, global_State *g) {
1171 GCObject **psurvival; /* to point to first non-dead survival object */ 1191 GCObject **psurvival; /* to point to first non-dead survival object */
1192 GCObject *dummy; /* dummy out parameter to 'sweepgen' */
1172 lua_assert(g->gcstate == GCSpropagate); 1193 lua_assert(g->gcstate == GCSpropagate);
1173 markold(g, g->allgc, g->reallyold); 1194 if (g->firstold1) { /* are there OLD1 objects? */
1195 markold(g, g->firstold1, g->reallyold); /* mark them */
1196 g->firstold1 = NULL; /* no more OLD1 objects (for now) */
1197 }
1174 markold(g, g->finobj, g->finobjrold); 1198 markold(g, g->finobj, g->finobjrold);
1175 atomic(L); 1199 atomic(L);
1176 1200
1177 /* sweep nursery and get a pointer to its last live element */ 1201 /* sweep nursery and get a pointer to its last live element */
1178 g->gcstate = GCSswpallgc; 1202 g->gcstate = GCSswpallgc;
1179 psurvival = sweepgen(L, g, &g->allgc, g->survival); 1203 psurvival = sweepgen(L, g, &g->allgc, g->survival, &g->firstold1);
1180 /* sweep 'survival' */ 1204 /* sweep 'survival' */
1181 sweepgen(L, g, psurvival, g->old1); 1205 sweepgen(L, g, psurvival, g->old1, &g->firstold1);
1182 g->reallyold = g->old1; 1206 g->reallyold = g->old1;
1183 g->old1 = *psurvival; /* 'survival' survivals are old now */ 1207 g->old1 = *psurvival; /* 'survival' survivals are old now */
1184 g->survival = g->allgc; /* all news are survivals */ 1208 g->survival = g->allgc; /* all news are survivals */
1185 1209
1186 /* repeat for 'finobj' lists */ 1210 /* repeat for 'finobj' lists */
1187 psurvival = sweepgen(L, g, &g->finobj, g->finobjsur); 1211 dummy = NULL; /* no 'firstold1' optimization for 'finobj' lists */
1212 psurvival = sweepgen(L, g, &g->finobj, g->finobjsur, &dummy);
1188 /* sweep 'survival' */ 1213 /* sweep 'survival' */
1189 sweepgen(L, g, psurvival, g->finobjold1); 1214 sweepgen(L, g, psurvival, g->finobjold1, &dummy);
1190 g->finobjrold = g->finobjold1; 1215 g->finobjrold = g->finobjold1;
1191 g->finobjold1 = *psurvival; /* 'survival' survivals are old now */ 1216 g->finobjold1 = *psurvival; /* 'survival' survivals are old now */
1192 g->finobjsur = g->finobj; /* all news are survivals */ 1217 g->finobjsur = g->finobj; /* all news are survivals */
1193 1218
1194 sweepgen(L, g, &g->tobefnz, NULL); 1219 sweepgen(L, g, &g->tobefnz, NULL, &dummy);
1195
1196 finishgencycle(L, g); 1220 finishgencycle(L, g);
1197} 1221}
1198 1222
@@ -1203,6 +1227,7 @@ static void atomic2gen (lua_State *L, global_State *g) {
1203 sweep2old(L, &g->allgc); 1227 sweep2old(L, &g->allgc);
1204 /* everything alive now is old */ 1228 /* everything alive now is old */
1205 g->reallyold = g->old1 = g->survival = g->allgc; 1229 g->reallyold = g->old1 = g->survival = g->allgc;
1230 g->firstold1 = NULL; /* there are no OLD1 objects anywhere */
1206 1231
1207 /* repeat for 'finobj' lists */ 1232 /* repeat for 'finobj' lists */
1208 sweep2old(L, &g->finobj); 1233 sweep2old(L, &g->finobj);
diff --git a/lstate.c b/lstate.c
index 38a2b45a..86b3761f 100644
--- a/lstate.c
+++ b/lstate.c
@@ -413,7 +413,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
413 g->gckind = KGC_INC; 413 g->gckind = KGC_INC;
414 g->gcemergency = 0; 414 g->gcemergency = 0;
415 g->finobj = g->tobefnz = g->fixedgc = NULL; 415 g->finobj = g->tobefnz = g->fixedgc = NULL;
416 g->survival = g->old1 = g->reallyold = NULL; 416 g->firstold1 = g->survival = g->old1 = g->reallyold = NULL;
417 g->finobjsur = g->finobjold1 = g->finobjrold = NULL; 417 g->finobjsur = g->finobjold1 = g->finobjrold = NULL;
418 g->sweepgc = NULL; 418 g->sweepgc = NULL;
419 g->gray = g->grayagain = NULL; 419 g->gray = g->grayagain = NULL;
diff --git a/lstate.h b/lstate.h
index c02b4c8b..697d73b2 100644
--- a/lstate.h
+++ b/lstate.h
@@ -46,6 +46,15 @@
46** lists. Moreover, barriers can age young objects in young lists as 46** lists. Moreover, barriers can age young objects in young lists as
47** OLD0, which then become OLD1. However, a list never contains 47** OLD0, which then become OLD1. However, a list never contains
48** elements younger than their main ages. 48** elements younger than their main ages.
49**
50** The generational collector also uses a pointer 'firstold1', which
51** points to the first OLD1 object in the list. It is used to optimize
52** 'markold'. (Potentially OLD1 objects can be anywhere between 'allgc'
53** and 'reallyold', but often the list has no OLD1 objects or they are
54** after 'old1'.) Note the difference between it and 'old1':
55** 'firstold1': no OLD1 objects before this point; there can be all
56** ages after it.
57** 'old1': no objects younger than OLD1 after this point.
49*/ 58*/
50 59
51/* 60/*
@@ -54,7 +63,7 @@
54** can become gray have such a field. The field is not the same 63** can become gray have such a field. The field is not the same
55** in all objects, but it always has this name.) Any gray object 64** in all objects, but it always has this name.) Any gray object
56** must belong to one of these lists, and all objects in these lists 65** must belong to one of these lists, and all objects in these lists
57** must be gray: 66** must be gray (with one exception explained below):
58** 67**
59** 'gray': regular gray objects, still waiting to be visited. 68** 'gray': regular gray objects, still waiting to be visited.
60** 'grayagain': objects that must be revisited at the atomic phase. 69** 'grayagain': objects that must be revisited at the atomic phase.
@@ -65,6 +74,12 @@
65** 'weak': tables with weak values to be cleared; 74** 'weak': tables with weak values to be cleared;
66** 'ephemeron': ephemeron tables with white->white entries; 75** 'ephemeron': ephemeron tables with white->white entries;
67** 'allweak': tables with weak keys and/or weak values to be cleared. 76** 'allweak': tables with weak keys and/or weak values to be cleared.
77**
78** The exception to that "gray rule" is the TOUCHED2 objects in
79** generational mode. Those objects stay in a gray list (because they
80** must be visited again at the end of the cycle), but they are marked
81** black (because assignments to them must activate barriers, to move
82** them back to TOUCHED1).
68*/ 83*/
69 84
70 85
@@ -266,6 +281,7 @@ typedef struct global_State {
266 GCObject *survival; /* start of objects that survived one GC cycle */ 281 GCObject *survival; /* start of objects that survived one GC cycle */
267 GCObject *old1; /* start of old1 objects */ 282 GCObject *old1; /* start of old1 objects */
268 GCObject *reallyold; /* objects more than one cycle old ("really old") */ 283 GCObject *reallyold; /* objects more than one cycle old ("really old") */
284 GCObject *firstold1; /* first OLD1 object in the list (if any) */
269 GCObject *finobjsur; /* list of survival objects with finalizers */ 285 GCObject *finobjsur; /* list of survival objects with finalizers */
270 GCObject *finobjold1; /* list of old1 objects with finalizers */ 286 GCObject *finobjold1; /* list of old1 objects with finalizers */
271 GCObject *finobjrold; /* list of really old objects with finalizers */ 287 GCObject *finobjrold; /* list of really old objects with finalizers */
diff --git a/testes/gengc.lua b/testes/gengc.lua
index 7a7dabdd..93b5afd7 100644
--- a/testes/gengc.lua
+++ b/testes/gengc.lua
@@ -37,6 +37,22 @@ do
37end 37end
38 38
39 39
40do
41 -- ensure that 'firstold1' is corrected when object is removed from
42 -- the 'allgc' list
43 local function foo () end
44 local old = {10}
45 collectgarbage() -- make 'old' old
46 assert(not T or T.gcage(old) == "old")
47 setmetatable(old, {}) -- new table becomes OLD0 (barrier)
48 assert(not T or T.gcage(getmetatable(old)) == "old0")
49 collectgarbage("step", 0) -- new table becomes OLD1 and firstold1
50 assert(not T or T.gcage(getmetatable(old)) == "old1")
51 setmetatable(getmetatable(old), {__gc = foo}) -- get it out of allgc list
52 collectgarbage("step", 0) -- should not seg. fault
53end
54
55
40do -- bug in 5.4.0 56do -- bug in 5.4.0
41-- When an object aged OLD1 is finalized, it is moved from the list 57-- When an object aged OLD1 is finalized, it is moved from the list
42-- 'finobj' to the *beginning* of the list 'allgc', but that part of the 58-- 'finobj' to the *beginning* of the list 'allgc', but that part of the