aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-01-30 11:44:42 -0200
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-01-30 11:44:42 -0200
commit2c32bff60987d38a60a58d4f0123f3783da60a63 (patch)
treedd97348ac388dd6087c320994bdce6ff18be02e2
parent264659bd53e92969a1e17d65c0266597cde24b5d (diff)
downloadlua-2c32bff60987d38a60a58d4f0123f3783da60a63.tar.gz
lua-2c32bff60987d38a60a58d4f0123f3783da60a63.tar.bz2
lua-2c32bff60987d38a60a58d4f0123f3783da60a63.zip
After a "bad collections", avoid switching back back to generational
After a major bad collection (one that collects too few objects), next collection will be major again. In that case, avoid switching back to generational mode (as it will have to switch again to incremental to do next major collection).
-rw-r--r--lapi.c6
-rw-r--r--lgc.c158
-rw-r--r--lgc.h9
-rw-r--r--lstate.c4
-rw-r--r--lstate.h1
-rw-r--r--testes/gc.lua6
6 files changed, 134 insertions, 50 deletions
diff --git a/lapi.c b/lapi.c
index 8ff7bfbd..4026497e 100644
--- a/lapi.c
+++ b/lapi.c
@@ -1141,22 +1141,21 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
1141 break; 1141 break;
1142 } 1142 }
1143 case LUA_GCGEN: { 1143 case LUA_GCGEN: {
1144 int oldmode = g->gckind;
1145 int minormul = va_arg(argp, int); 1144 int minormul = va_arg(argp, int);
1146 int majormul = va_arg(argp, int); 1145 int majormul = va_arg(argp, int);
1146 res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
1147 if (minormul != 0) 1147 if (minormul != 0)
1148 g->genminormul = minormul; 1148 g->genminormul = minormul;
1149 if (majormul != 0) 1149 if (majormul != 0)
1150 setgcparam(g->genmajormul, majormul); 1150 setgcparam(g->genmajormul, majormul);
1151 luaC_changemode(L, KGC_GEN); 1151 luaC_changemode(L, KGC_GEN);
1152 res = (oldmode == KGC_GEN) ? LUA_GCGEN : LUA_GCINC;
1153 break; 1152 break;
1154 } 1153 }
1155 case LUA_GCINC: { 1154 case LUA_GCINC: {
1156 int oldmode = g->gckind;
1157 int pause = va_arg(argp, int); 1155 int pause = va_arg(argp, int);
1158 int stepmul = va_arg(argp, int); 1156 int stepmul = va_arg(argp, int);
1159 int stepsize = va_arg(argp, int); 1157 int stepsize = va_arg(argp, int);
1158 res = isdecGCmodegen(g) ? LUA_GCGEN : LUA_GCINC;
1160 if (pause != 0) 1159 if (pause != 0)
1161 setgcparam(g->gcpause, pause); 1160 setgcparam(g->gcpause, pause);
1162 if (stepmul != 0) 1161 if (stepmul != 0)
@@ -1164,7 +1163,6 @@ LUA_API int lua_gc (lua_State *L, int what, ...) {
1164 if (stepsize != 0) 1163 if (stepsize != 0)
1165 g->gcstepsize = stepsize; 1164 g->gcstepsize = stepsize;
1166 luaC_changemode(L, KGC_INC); 1165 luaC_changemode(L, KGC_INC);
1167 res = (oldmode == KGC_GEN) ? LUA_GCGEN : LUA_GCINC;
1168 break; 1166 break;
1169 } 1167 }
1170 default: res = -1; /* invalid option */ 1168 default: res = -1; /* invalid option */
diff --git a/lgc.c b/lgc.c
index 0c3386e7..bf0460d5 100644
--- a/lgc.c
+++ b/lgc.c
@@ -101,6 +101,7 @@
101 101
102static void reallymarkobject (global_State *g, GCObject *o); 102static void reallymarkobject (global_State *g, GCObject *o);
103static lu_mem atomic (lua_State *L); 103static lu_mem atomic (lua_State *L);
104static void entersweep (lua_State *L);
104 105
105 106
106/* 107/*
@@ -1162,15 +1163,7 @@ static void youngcollection (lua_State *L, global_State *g) {
1162} 1163}
1163 1164
1164 1165
1165/* 1166static void atomic2gen (lua_State *L, global_State *g) {
1166** Enter generational mode. Must go until the end of an atomic cycle
1167** to ensure that all threads are in the gray list. Then, turn all
1168** objects into old and finishes the collection.
1169*/
1170static void entergen (lua_State *L, global_State *g) {
1171 luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
1172 luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
1173 atomic(L);
1174 /* sweep all elements making them old */ 1167 /* sweep all elements making them old */
1175 sweep2old(L, &g->allgc); 1168 sweep2old(L, &g->allgc);
1176 /* everything alive now is old */ 1169 /* everything alive now is old */
@@ -1183,15 +1176,31 @@ static void entergen (lua_State *L, global_State *g) {
1183 sweep2old(L, &g->tobefnz); 1176 sweep2old(L, &g->tobefnz);
1184 1177
1185 g->gckind = KGC_GEN; 1178 g->gckind = KGC_GEN;
1179 g->lastatomic = 0;
1186 g->GCestimate = gettotalbytes(g); /* base for memory control */ 1180 g->GCestimate = gettotalbytes(g); /* base for memory control */
1187 finishgencycle(L, g); 1181 finishgencycle(L, g);
1188} 1182}
1189 1183
1190 1184
1191/* 1185/*
1186** Enter generational mode. Must go until the end of an atomic cycle
1187** to ensure that all threads and weak tables are in the gray lists.
1188** Then, turn all objects into old and finishes the collection.
1189*/
1190static lu_mem entergen (lua_State *L, global_State *g) {
1191 lu_mem numobjs;
1192 luaC_runtilstate(L, bitmask(GCSpause)); /* prepare to start a new cycle */
1193 luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
1194 numobjs = atomic(L); /* propagates all and then do the atomic stuff */
1195 atomic2gen(L, g);
1196 return numobjs;
1197}
1198
1199
1200/*
1192** Enter incremental mode. Turn all objects white, make all 1201** Enter incremental mode. Turn all objects white, make all
1193** intermediate lists point to NULL (to avoid invalid pointers), 1202** intermediate lists point to NULL (to avoid invalid pointers),
1194** and go to pause state. 1203** and go to the pause state.
1195*/ 1204*/
1196static void enterinc (global_State *g) { 1205static void enterinc (global_State *g) {
1197 whitelist(g, g->allgc); 1206 whitelist(g, g->allgc);
@@ -1201,6 +1210,7 @@ static void enterinc (global_State *g) {
1201 g->finobjrold = g->finobjold = g->finobjsur = NULL; 1210 g->finobjrold = g->finobjold = g->finobjsur = NULL;
1202 g->gcstate = GCSpause; 1211 g->gcstate = GCSpause;
1203 g->gckind = KGC_INC; 1212 g->gckind = KGC_INC;
1213 g->lastatomic = 0;
1204} 1214}
1205 1215
1206 1216
@@ -1215,54 +1225,114 @@ void luaC_changemode (lua_State *L, int newmode) {
1215 else 1225 else
1216 enterinc(g); /* entering incremental mode */ 1226 enterinc(g); /* entering incremental mode */
1217 } 1227 }
1228 g->lastatomic = 0;
1218} 1229}
1219 1230
1220 1231
1221/* 1232/*
1222** Does a full collection in generational mode. 1233** Does a full collection in generational mode.
1223*/ 1234*/
1224static void fullgen (lua_State *L, global_State *g) { 1235static lu_mem fullgen (lua_State *L, global_State *g) {
1225 enterinc(g); 1236 enterinc(g);
1226 entergen(L, g); 1237 return entergen(L, g);
1238}
1239
1240
1241/*
1242** Set debt for the next minor collection, which will happen when
1243** memory grows 'genminormul'%.
1244*/
1245static void setminordebt (global_State *g) {
1246 luaE_setdebt(g, -(cast(l_mem, (gettotalbytes(g) / 100)) * g->genminormul));
1227} 1247}
1228 1248
1229 1249
1230/* 1250/*
1231** Does a generational "step". If memory grows 'genmajormul'% larger 1251** Does a major collection after last collection was a "bad collection".
1232** than last major collection (kept in 'g->GCestimate'), does a major 1252**
1233** collection. Otherwise, does a minor collection and set debt to make 1253** When the program is building a big struture, it allocates lots of
1234** another collection when memory grows 'genminormul'% larger. 1254** memory but generates very little garbage. In those scenarios,
1235** When it does a major collection, it then checks whether it could 1255** the generational mode just wastes time doing small collections, and
1236** reclaim at least ?? memory. If not, it sets a long pause for the 1256** major collections are frequently what we call a "bad collection", a
1237** next collection. (Therefore, the next collection will be a major 1257** collection that frees too few objects. To avoid the cost of switching
1238** one, too.) 1258** between generational mode and the incremental mode needed for full
1259** (major) collections, the collector tries to stay in incremental mode
1260** after a bad collection, and to switch back to generational mode only
1261** after a "good" collection (one that traverses less than 9/8 objects
1262** of the previous one).
1263** The collector must choose whether to stay in incremental mode or to
1264** switch back to generational mode before sweeping. At this point, it
1265** does not know the real memory in use, so it cannot use memory to
1266** decide whether to return to generational mode. Instead, it uses the
1267** number of objects traversed (returned by 'atomic') as a proxy. The
1268** field 'g->lastatomic' keeps this count from the last collection.
1269** ('g->lastatomic != 0' also means that the last collection was bad.)
1270*/
1271static void stepgenfull (lua_State *L, global_State *g) {
1272 lu_mem newatomic; /* count of traversed objects */
1273 lu_mem lastatomic = g->lastatomic; /* count from last collection */
1274 if (g->gckind == KGC_GEN) /* still in generational mode? */
1275 enterinc(g); /* enter incremental mode */
1276 luaC_runtilstate(L, bitmask(GCSpropagate)); /* start new cycle */
1277 newatomic = atomic(L); /* mark everybody */
1278 if (newatomic < lastatomic + (lastatomic >> 3)) { /* good collection? */
1279 atomic2gen(L, g); /* return to generational mode */
1280 setminordebt(g);
1281 }
1282 else { /* another bad collection; stay in incremental mode */
1283 g->GCestimate = gettotalbytes(g); /* first estimate */;
1284 entersweep(L);
1285 luaC_runtilstate(L, bitmask(GCSpause)); /* finish collection */
1286 setpause(g);
1287 g->lastatomic = newatomic;
1288 }
1289}
1290
1291
1292/*
1293** Does a generational "step".
1294** Usually, this means doing a minor collection and setting the debt to
1295** make another collection when memory grows 'genminormul'% larger.
1296**
1297** However, there are exceptions. If memory grows 'genmajormul'%
1298** larger than it was at the end of the last major collection (kept
1299** in 'g->GCestimate'), the function does a major collection. At the
1300** end, it checks whether the major collection was able to free a
1301** decent amount of memory (at least half the growth in memory since
1302** previous major collection). If so, the collector keeps its state,
1303** and the next collection will probably be minor again. Otherwise,
1304** we have what we call a "bad collection". In that case, set the field
1305** 'g->lastatomic' to signal that fact, so that the next collection will
1306** go to 'stepgenfull'.
1307**
1239** 'GCdebt <= 0' means an explicit call to GC step with "size" zero; 1308** 'GCdebt <= 0' means an explicit call to GC step with "size" zero;
1240** in that case, always do a minor collection. 1309** in that case, do a minor collection.
1241*/ 1310*/
1242static void genstep (lua_State *L, global_State *g) { 1311static void genstep (lua_State *L, global_State *g) {
1243 lu_mem majorbase = g->GCestimate; /* memory after last major collection */ 1312 if (g->lastatomic != 0) /* last collection was a bad one? */
1244 lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul); 1313 stepgenfull(L, g); /* do a full step */
1245 lu_mem memnew = gettotalbytes(g); 1314 else {
1246 if (g->GCdebt > 0 && memnew > majorbase + majorinc) { 1315 lu_mem majorbase = g->GCestimate; /* memory after last major collection */
1247 fullgen(L, g); 1316 lu_mem majorinc = (majorbase / 100) * getgcparam(g->genmajormul);
1248 memnew = gettotalbytes(g); 1317 if (g->GCdebt > 0 && gettotalbytes(g) > majorbase + majorinc) {
1249 if (memnew < majorbase + (majorinc / 2)) { 1318 lu_mem numobjs = fullgen(L, g); /* do a major collection */
1250 /* collected at least half of memory growth since last major 1319 if (gettotalbytes(g) < majorbase + (majorinc / 2)) {
1251 collection; go back to minor collections */ 1320 /* collected at least half of memory growth since last major
1252 luaE_setdebt(g, -(cast(l_mem, (memnew / 100)) * g->genminormul)); 1321 collection; keep doing minor collections */
1322 setminordebt(g);
1323 }
1324 else { /* bad collection */
1325 g->lastatomic = numobjs; /* signal that last collection was bad */
1326 setpause(g); /* do a long wait for next (major) collection */
1327 }
1253 } 1328 }
1254 else { 1329 else { /* regular case; do a minor collection */
1255 /* memory seems to be growing; do a long wait for next (major) 1330 youngcollection(L, g);
1256 collection */ 1331 setminordebt(g);
1257 setpause(g); 1332 g->GCestimate = majorbase; /* preserve base value */
1258 } 1333 }
1259 } 1334 }
1260 else { 1335 lua_assert(isdecGCmodegen(g));
1261 youngcollection(L, g);
1262 memnew = gettotalbytes(g);
1263 luaE_setdebt(g, -(cast(l_mem, (memnew / 100)) * g->genminormul));
1264 g->GCestimate = majorbase; /* preserve base value */
1265 }
1266} 1336}
1267 1337
1268/* }====================================================== */ 1338/* }====================================================== */
@@ -1493,10 +1563,10 @@ static void incstep (lua_State *L, global_State *g) {
1493void luaC_step (lua_State *L) { 1563void luaC_step (lua_State *L) {
1494 global_State *g = G(L); 1564 global_State *g = G(L);
1495 if (g->gcrunning) { /* running? */ 1565 if (g->gcrunning) { /* running? */
1496 if (g->gckind == KGC_INC) 1566 if(isdecGCmodegen(g))
1497 incstep(L, g);
1498 else
1499 genstep(L, g); 1567 genstep(L, g);
1568 else
1569 incstep(L, g);
1500 } 1570 }
1501} 1571}
1502 1572
diff --git a/lgc.h b/lgc.h
index 6b1c2861..9ba7ecb0 100644
--- a/lgc.h
+++ b/lgc.h
@@ -123,7 +123,7 @@
123#define LUAI_GENMINORMUL 20 123#define LUAI_GENMINORMUL 20
124 124
125/* wait memory to double before starting new cycle */ 125/* wait memory to double before starting new cycle */
126#define LUAI_GCPAUSE 200 /* 200% */ 126#define LUAI_GCPAUSE 200
127 127
128/* 128/*
129** some gc parameters are stored divided by 4 to allow a maximum value 129** some gc parameters are stored divided by 4 to allow a maximum value
@@ -139,6 +139,13 @@
139 139
140 140
141/* 141/*
142** Check whether the declared GC mode is generational. While in
143** generational mode, the collector can go temporarily to incremental
144** mode to improve performance. This is signaled by 'g->lastatomic != 0'.
145*/
146#define isdecGCmodegen(g) (g->gckind == KGC_GEN || g->lastatomic != 0)
147
148/*
142** Does one step of collection when debt becomes positive. 'pre'/'pos' 149** Does one step of collection when debt becomes positive. 'pre'/'pos'
143** allows some adjustments to be done only when needed. macro 150** allows some adjustments to be done only when needed. macro
144** 'condchangemem' is used only for heavy tests (forcing a full 151** 'condchangemem' is used only for heavy tests (forcing a full
diff --git a/lstate.c b/lstate.c
index 7f6475a8..6c35ea1a 100644
--- a/lstate.c
+++ b/lstate.c
@@ -233,7 +233,8 @@ static void init_registry (lua_State *L, global_State *g) {
233 233
234/* 234/*
235** open parts of the state that may cause memory-allocation errors. 235** open parts of the state that may cause memory-allocation errors.
236** ('ttisnil(&g->nilvalue)'' flags that the state was completely build) 236** ('g->nilvalue' being a nil value flags that the state was completely
237** build.)
237*/ 238*/
238static void f_luaopen (lua_State *L, void *ud) { 239static void f_luaopen (lua_State *L, void *ud) {
239 global_State *g = G(L); 240 global_State *g = G(L);
@@ -386,6 +387,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
386 g->twups = NULL; 387 g->twups = NULL;
387 g->totalbytes = sizeof(LG); 388 g->totalbytes = sizeof(LG);
388 g->GCdebt = 0; 389 g->GCdebt = 0;
390 g->lastatomic = 0;
389 setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */ 391 setivalue(&g->nilvalue, 0); /* to signal that state is not yet built */
390 setgcparam(g->gcpause, LUAI_GCPAUSE); 392 setgcparam(g->gcpause, LUAI_GCPAUSE);
391 setgcparam(g->gcstepmul, LUAI_GCMUL); 393 setgcparam(g->gcstepmul, LUAI_GCMUL);
diff --git a/lstate.h b/lstate.h
index 05a74dda..11bf18fd 100644
--- a/lstate.h
+++ b/lstate.h
@@ -193,6 +193,7 @@ typedef struct global_State {
193 l_mem totalbytes; /* number of bytes currently allocated - GCdebt */ 193 l_mem totalbytes; /* number of bytes currently allocated - GCdebt */
194 l_mem GCdebt; /* bytes allocated not yet compensated by the collector */ 194 l_mem GCdebt; /* bytes allocated not yet compensated by the collector */
195 lu_mem GCestimate; /* an estimate of the non-garbage memory in use */ 195 lu_mem GCestimate; /* an estimate of the non-garbage memory in use */
196 lu_mem lastatomic; /* see function 'genstep' in file 'lgc.c' */
196 stringtable strt; /* hash table for strings */ 197 stringtable strt; /* hash table for strings */
197 TValue l_registry; 198 TValue l_registry;
198 TValue nilvalue; /* a nil value */ 199 TValue nilvalue; /* a nil value */
diff --git a/testes/gc.lua b/testes/gc.lua
index 84e8ffb7..05bf564e 100644
--- a/testes/gc.lua
+++ b/testes/gc.lua
@@ -11,6 +11,12 @@ collectgarbage()
11 11
12local oldmode = collectgarbage("incremental") 12local oldmode = collectgarbage("incremental")
13 13
14-- changing modes should return previous mode
15assert(collectgarbage("generational") == "incremental")
16assert(collectgarbage("generational") == "generational")
17assert(collectgarbage("incremental") == "generational")
18assert(collectgarbage("incremental") == "incremental")
19
14 20
15local function gcinfo () 21local function gcinfo ()
16 return collectgarbage"count" * 1024 22 return collectgarbage"count" * 1024