diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-01 13:21:00 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2018-11-01 13:21:00 -0300 |
commit | e8c779736f3029df353038352c14c8ab63728811 (patch) | |
tree | 23f75fa455ec333677650198cd889b187228b735 /lgc.c | |
parent | 2fc6b55dae7a120b4272ca0e9c356d1f96053bd9 (diff) | |
download | lua-e8c779736f3029df353038352c14c8ab63728811.tar.gz lua-e8c779736f3029df353038352c14c8ab63728811.tar.bz2 lua-e8c779736f3029df353038352c14c8ab63728811.zip |
Removed internal cache for closures
The mechanism of "caching the last closure created for a prototype to
try to reuse it the next time a closure for that prototype is created"
was removed. There are several reasons:
- It is hard to find a natural example where this cache has a measurable
impact on performance.
- Programmers already perceive closure creation as something slow,
so they tend to avoid it inside hot paths. (Any case where the cache
could reuse a closure can be rewritten predefining the closure in some
variable and using that variable.)
- The implementation was somewhat complex, due to a bad interaction
with the generational collector. (Typically, new closures are new,
while prototypes are old. So, the cache breaks the invariant that
old objects should not point to new ones.)
Diffstat (limited to '')
-rw-r--r-- | lgc.c | 64 |
1 files changed, 1 insertions, 63 deletions
@@ -221,27 +221,6 @@ void luaC_barrierback_ (lua_State *L, GCObject *o) { | |||
221 | } | 221 | } |
222 | 222 | ||
223 | 223 | ||
224 | /* | ||
225 | ** Barrier for prototype's cache of closures. It turns the prototype | ||
226 | ** back to gray (it was black). For an 'OLD1' prototype, making it | ||
227 | ** gray stops it from being visited by 'markold', so it is linked in | ||
228 | ** the 'grayagain' list to ensure it will be visited. For other ages, | ||
229 | ** it goes to the 'protogray' list, as only its 'cache' field needs to | ||
230 | ** be revisited. (A prototype to be in this barrier must be already | ||
231 | ** finished, so its other fields cannot change and do not need to be | ||
232 | ** revisited.) | ||
233 | */ | ||
234 | LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p) { | ||
235 | global_State *g = G(L); | ||
236 | lua_assert(g->gckind != KGC_GEN || isold(p)); | ||
237 | if (getage(p) == G_OLD1) /* still need to be visited? */ | ||
238 | linkgclist(p, g->grayagain); /* link it in 'grayagain' */ | ||
239 | else | ||
240 | linkgclist(p, g->protogray); /* link it in 'protogray' */ | ||
241 | black2gray(p); /* make prototype gray */ | ||
242 | } | ||
243 | |||
244 | |||
245 | void luaC_fix (lua_State *L, GCObject *o) { | 224 | void luaC_fix (lua_State *L, GCObject *o) { |
246 | global_State *g = G(L); | 225 | global_State *g = G(L); |
247 | lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ | 226 | lua_assert(g->allgc == o); /* object must be 1st in 'allgc' list! */ |
@@ -379,7 +358,7 @@ static int remarkupvals (global_State *g) { | |||
379 | */ | 358 | */ |
380 | static void restartcollection (global_State *g) { | 359 | static void restartcollection (global_State *g) { |
381 | g->gray = g->grayagain = NULL; | 360 | g->gray = g->grayagain = NULL; |
382 | g->weak = g->allweak = g->ephemeron = g->protogray = NULL; | 361 | g->weak = g->allweak = g->ephemeron = NULL; |
383 | markobject(g, g->mainthread); | 362 | markobject(g, g->mainthread); |
384 | markvalue(g, &g->l_registry); | 363 | markvalue(g, &g->l_registry); |
385 | markmt(g); | 364 | markmt(g); |
@@ -535,39 +514,12 @@ static int traverseudata (global_State *g, Udata *u) { | |||
535 | 514 | ||
536 | 515 | ||
537 | /* | 516 | /* |
538 | ** Check the cache of a prototype, to keep invariants. If the | ||
539 | ** cache is white, clear it. (A cache should not prevent the | ||
540 | ** collection of its reference.) Otherwise, if in generational | ||
541 | ** mode, check the generational invariant. If the cache is old, | ||
542 | ** everything is ok. If the prototype is 'OLD0', everything | ||
543 | ** is ok too. (It will naturally be visited again.) If the | ||
544 | ** prototype is older than 'OLD0', then its cache (which is new) | ||
545 | ** must be visited again in the next collection, so the prototype | ||
546 | ** goes to the 'protogray' list. (If the prototype has a cache, | ||
547 | ** it is already immutable and does not need other barriers; | ||
548 | ** then, it can become gray without problems for its other fields.) | ||
549 | */ | ||
550 | static void checkprotocache (global_State *g, Proto *p) { | ||
551 | if (p->cache) { | ||
552 | if (iswhite(p->cache)) | ||
553 | p->cache = NULL; /* allow cache to be collected */ | ||
554 | else if (g->gckind == KGC_GEN && !isold(p->cache) && getage(p) >= G_OLD1) { | ||
555 | linkgclist(p, g->protogray); /* link it in 'protogray' */ | ||
556 | black2gray(p); /* make prototype gray */ | ||
557 | } | ||
558 | } | ||
559 | p->cachemiss = 0; /* restart counting */ | ||
560 | } | ||
561 | |||
562 | |||
563 | /* | ||
564 | ** Traverse a prototype. (While a prototype is being build, its | 517 | ** Traverse a prototype. (While a prototype is being build, its |
565 | ** arrays can be larger than needed; the extra slots are filled with | 518 | ** arrays can be larger than needed; the extra slots are filled with |
566 | ** NULL, so the use of 'markobjectN') | 519 | ** NULL, so the use of 'markobjectN') |
567 | */ | 520 | */ |
568 | static int traverseproto (global_State *g, Proto *f) { | 521 | static int traverseproto (global_State *g, Proto *f) { |
569 | int i; | 522 | int i; |
570 | checkprotocache(g, f); | ||
571 | markobjectN(g, f->source); | 523 | markobjectN(g, f->source); |
572 | for (i = 0; i < f->sizek; i++) /* mark literals */ | 524 | for (i = 0; i < f->sizek; i++) /* mark literals */ |
573 | markvalue(g, &f->k[i]); | 525 | markvalue(g, &f->k[i]); |
@@ -696,19 +648,6 @@ static void convergeephemerons (global_State *g) { | |||
696 | ** ======================================================= | 648 | ** ======================================================= |
697 | */ | 649 | */ |
698 | 650 | ||
699 | static void clearprotolist (global_State *g) { | ||
700 | GCObject *p = g->protogray; | ||
701 | g->protogray = NULL; | ||
702 | while (p != NULL) { | ||
703 | Proto *pp = gco2p(p); | ||
704 | GCObject *next = pp->gclist; | ||
705 | lua_assert(isgray(pp) && (pp->cache != NULL || pp->cachemiss >= MAXMISS)); | ||
706 | gray2black(pp); | ||
707 | checkprotocache(g, pp); | ||
708 | p = next; | ||
709 | } | ||
710 | } | ||
711 | |||
712 | 651 | ||
713 | /* | 652 | /* |
714 | ** clear entries with unmarked keys from all weaktables in list 'l' | 653 | ** clear entries with unmarked keys from all weaktables in list 'l' |
@@ -1439,7 +1378,6 @@ static lu_mem atomic (lua_State *L) { | |||
1439 | clearbyvalues(g, g->weak, origweak); | 1378 | clearbyvalues(g, g->weak, origweak); |
1440 | clearbyvalues(g, g->allweak, origall); | 1379 | clearbyvalues(g, g->allweak, origall); |
1441 | luaS_clearcache(g); | 1380 | luaS_clearcache(g); |
1442 | clearprotolist(g); | ||
1443 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ | 1381 | g->currentwhite = cast_byte(otherwhite(g)); /* flip current white */ |
1444 | lua_assert(g->gray == NULL); | 1382 | lua_assert(g->gray == NULL); |
1445 | return work; /* estimate of slots marked by 'atomic' */ | 1383 | return work; /* estimate of slots marked by 'atomic' */ |