diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-01-30 11:44:42 -0200 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-01-30 11:44:42 -0200 |
commit | 2c32bff60987d38a60a58d4f0123f3783da60a63 (patch) | |
tree | dd97348ac388dd6087c320994bdce6ff18be02e2 /lgc.c | |
parent | 264659bd53e92969a1e17d65c0266597cde24b5d (diff) | |
download | lua-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).
Diffstat (limited to 'lgc.c')
-rw-r--r-- | lgc.c | 158 |
1 files changed, 114 insertions, 44 deletions
@@ -101,6 +101,7 @@ | |||
101 | 101 | ||
102 | static void reallymarkobject (global_State *g, GCObject *o); | 102 | static void reallymarkobject (global_State *g, GCObject *o); |
103 | static lu_mem atomic (lua_State *L); | 103 | static lu_mem atomic (lua_State *L); |
104 | static 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 | /* | 1166 | static 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 | */ | ||
1170 | static 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 | */ | ||
1190 | static 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 | */ |
1196 | static void enterinc (global_State *g) { | 1205 | static 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 | */ |
1224 | static void fullgen (lua_State *L, global_State *g) { | 1235 | static 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 | */ | ||
1245 | static 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 | */ | ||
1271 | static 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 | */ |
1242 | static void genstep (lua_State *L, global_State *g) { | 1311 | static 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) { | |||
1493 | void luaC_step (lua_State *L) { | 1563 | void 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 | ||