aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-11-01 13:21:00 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2018-11-01 13:21:00 -0300
commite8c779736f3029df353038352c14c8ab63728811 (patch)
tree23f75fa455ec333677650198cd889b187228b735
parent2fc6b55dae7a120b4272ca0e9c356d1f96053bd9 (diff)
downloadlua-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--lfunc.c2
-rw-r--r--lgc.c64
-rw-r--r--lgc.h4
-rw-r--r--lobject.h2
-rw-r--r--lstate.c2
-rw-r--r--lstate.h3
-rw-r--r--ltests.c4
-rw-r--r--lvm.c39
-rw-r--r--testes/closure.lua13
9 files changed, 10 insertions, 123 deletions
diff --git a/lfunc.c b/lfunc.c
index 15874f88..aa6ce58f 100644
--- a/lfunc.c
+++ b/lfunc.c
@@ -219,8 +219,6 @@ Proto *luaF_newproto (lua_State *L) {
219 f->p = NULL; 219 f->p = NULL;
220 f->sizep = 0; 220 f->sizep = 0;
221 f->code = NULL; 221 f->code = NULL;
222 f->cache = NULL;
223 f->cachemiss = 0;
224 f->sizecode = 0; 222 f->sizecode = 0;
225 f->lineinfo = NULL; 223 f->lineinfo = NULL;
226 f->sizelineinfo = 0; 224 f->sizelineinfo = 0;
diff --git a/lgc.c b/lgc.c
index 9d196a18..95a8ad5b 100644
--- a/lgc.c
+++ b/lgc.c
@@ -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*/
234LUAI_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
245void luaC_fix (lua_State *L, GCObject *o) { 224void 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*/
380static void restartcollection (global_State *g) { 359static 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*/
550static 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*/
568static int traverseproto (global_State *g, Proto *f) { 521static 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
699static 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' */
diff --git a/lgc.h b/lgc.h
index e0a8806b..6b1c2861 100644
--- a/lgc.h
+++ b/lgc.h
@@ -164,9 +164,6 @@
164 (isblack(p) && iswhite(o)) ? \ 164 (isblack(p) && iswhite(o)) ? \
165 luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0)) 165 luaC_barrier_(L,obj2gco(p),obj2gco(o)) : cast_void(0))
166 166
167#define luaC_protobarrier(L,p,o) \
168 (isblack(p) ? luaC_protobarrier_(L,p) : cast_void(0))
169
170LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o); 167LUAI_FUNC void luaC_fix (lua_State *L, GCObject *o);
171LUAI_FUNC void luaC_freeallobjects (lua_State *L); 168LUAI_FUNC void luaC_freeallobjects (lua_State *L);
172LUAI_FUNC void luaC_step (lua_State *L); 169LUAI_FUNC void luaC_step (lua_State *L);
@@ -175,7 +172,6 @@ LUAI_FUNC void luaC_fullgc (lua_State *L, int isemergency);
175LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz); 172LUAI_FUNC GCObject *luaC_newobj (lua_State *L, int tt, size_t sz);
176LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v); 173LUAI_FUNC void luaC_barrier_ (lua_State *L, GCObject *o, GCObject *v);
177LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o); 174LUAI_FUNC void luaC_barrierback_ (lua_State *L, GCObject *o);
178LUAI_FUNC void luaC_protobarrier_ (lua_State *L, Proto *p);
179LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt); 175LUAI_FUNC void luaC_checkfinalizer (lua_State *L, GCObject *o, Table *mt);
180LUAI_FUNC void luaC_changemode (lua_State *L, int newmode); 176LUAI_FUNC void luaC_changemode (lua_State *L, int newmode);
181 177
diff --git a/lobject.h b/lobject.h
index ea3511de..df31a1a0 100644
--- a/lobject.h
+++ b/lobject.h
@@ -505,7 +505,6 @@ typedef struct Proto {
505 lu_byte numparams; /* number of fixed (named) parameters */ 505 lu_byte numparams; /* number of fixed (named) parameters */
506 lu_byte is_vararg; 506 lu_byte is_vararg;
507 lu_byte maxstacksize; /* number of registers needed by this function */ 507 lu_byte maxstacksize; /* number of registers needed by this function */
508 lu_byte cachemiss; /* count for successive misses for 'cache' field */
509 int sizeupvalues; /* size of 'upvalues' */ 508 int sizeupvalues; /* size of 'upvalues' */
510 int sizek; /* size of 'k' */ 509 int sizek; /* size of 'k' */
511 int sizecode; 510 int sizecode;
@@ -516,7 +515,6 @@ typedef struct Proto {
516 int linedefined; /* debug information */ 515 int linedefined; /* debug information */
517 int lastlinedefined; /* debug information */ 516 int lastlinedefined; /* debug information */
518 TValue *k; /* constants used by the function */ 517 TValue *k; /* constants used by the function */
519 struct LClosure *cache; /* last-created closure with this prototype */
520 Instruction *code; /* opcodes */ 518 Instruction *code; /* opcodes */
521 struct Proto **p; /* functions defined inside the function */ 519 struct Proto **p; /* functions defined inside the function */
522 Upvaldesc *upvalues; /* upvalue information */ 520 Upvaldesc *upvalues; /* upvalue information */
diff --git a/lstate.c b/lstate.c
index 4a2453d1..9d399959 100644
--- a/lstate.c
+++ b/lstate.c
@@ -340,7 +340,7 @@ LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) {
340 g->finobjsur = g->finobjold = g->finobjrold = NULL; 340 g->finobjsur = g->finobjold = g->finobjrold = NULL;
341 g->sweepgc = NULL; 341 g->sweepgc = NULL;
342 g->gray = g->grayagain = NULL; 342 g->gray = g->grayagain = NULL;
343 g->weak = g->ephemeron = g->allweak = g->protogray = NULL; 343 g->weak = g->ephemeron = g->allweak = NULL;
344 g->twups = NULL; 344 g->twups = NULL;
345 g->totalbytes = sizeof(LG); 345 g->totalbytes = sizeof(LG);
346 g->GCdebt = 0; 346 g->GCdebt = 0;
diff --git a/lstate.h b/lstate.h
index f08c2355..ce337707 100644
--- a/lstate.h
+++ b/lstate.h
@@ -43,8 +43,6 @@
43** 'weak': tables with weak values to be cleared; 43** 'weak': tables with weak values to be cleared;
44** 'ephemeron': ephemeron tables with white->white entries; 44** 'ephemeron': ephemeron tables with white->white entries;
45** 'allweak': tables with weak keys and/or weak values to be cleared. 45** 'allweak': tables with weak keys and/or weak values to be cleared.
46** There is also a list 'protogray' for prototypes that need to have
47** their caches cleared.
48 46
49*/ 47*/
50 48
@@ -186,7 +184,6 @@ typedef struct global_State {
186 GCObject *weak; /* list of tables with weak values */ 184 GCObject *weak; /* list of tables with weak values */
187 GCObject *ephemeron; /* list of ephemeron tables (weak keys) */ 185 GCObject *ephemeron; /* list of ephemeron tables (weak keys) */
188 GCObject *allweak; /* list of all-weak tables */ 186 GCObject *allweak; /* list of all-weak tables */
189 GCObject *protogray; /* list of prototypes with "new" caches */
190 GCObject *tobefnz; /* list of userdata to be GC */ 187 GCObject *tobefnz; /* list of userdata to be GC */
191 GCObject *fixedgc; /* list of objects not to be collected */ 188 GCObject *fixedgc; /* list of objects not to be collected */
192 /* fields for generational collector */ 189 /* fields for generational collector */
diff --git a/ltests.c b/ltests.c
index 7cf08891..aba44b87 100644
--- a/ltests.c
+++ b/ltests.c
@@ -283,7 +283,6 @@ static void checkudata (global_State *g, Udata *u) {
283static void checkproto (global_State *g, Proto *f) { 283static void checkproto (global_State *g, Proto *f) {
284 int i; 284 int i;
285 GCObject *fgc = obj2gco(f); 285 GCObject *fgc = obj2gco(f);
286 checkobjref(g, fgc, f->cache);
287 checkobjref(g, fgc, f->source); 286 checkobjref(g, fgc, f->source);
288 for (i=0; i<f->sizek; i++) { 287 for (i=0; i<f->sizek; i++) {
289 if (ttisstring(f->k + i)) 288 if (ttisstring(f->k + i))
@@ -417,8 +416,6 @@ static void checkobject (global_State *g, GCObject *o, int maybedead,
417 getage(o) == G_TOUCHED1 || 416 getage(o) == G_TOUCHED1 ||
418 getage(o) == G_OLD0 || 417 getage(o) == G_OLD0 ||
419 o->tt == LUA_TTHREAD || 418 o->tt == LUA_TTHREAD ||
420 (o->tt == LUA_TPROTO &&
421 (gco2p(o)->cache != NULL || gco2p(o)->cachemiss >= MAXMISS)) ||
422 (o->tt == LUA_TUPVAL && upisopen(gco2upv(o)))); 419 (o->tt == LUA_TUPVAL && upisopen(gco2upv(o))));
423 } 420 }
424 } 421 }
@@ -452,7 +449,6 @@ static void checkgrays (global_State *g) {
452 checkgraylist(g, g->grayagain); 449 checkgraylist(g, g->grayagain);
453 checkgraylist(g, g->weak); 450 checkgraylist(g, g->weak);
454 checkgraylist(g, g->ephemeron); 451 checkgraylist(g, g->ephemeron);
455 checkgraylist(g, g->protogray);
456} 452}
457 453
458 454
diff --git a/lvm.c b/lvm.c
index 1535700f..d9671055 100644
--- a/lvm.c
+++ b/lvm.c
@@ -684,30 +684,8 @@ lua_Integer luaV_shiftl (lua_Integer x, lua_Integer y) {
684 684
685 685
686/* 686/*
687** check whether cached closure in prototype 'p' may be reused, that is,
688** whether there is a cached closure with the same upvalues needed by
689** new closure to be created.
690*/
691static LClosure *getcached (Proto *p, UpVal **encup, StkId base) {
692 LClosure *c = p->cache;
693 if (c != NULL) { /* is there a cached closure? */
694 int nup = p->sizeupvalues;
695 Upvaldesc *uv = p->upvalues;
696 int i;
697 for (i = 0; i < nup; i++) { /* check whether it has right upvalues */
698 TValue *v = uv[i].instack ? s2v(base + uv[i].idx) : encup[uv[i].idx]->v;
699 if (c->upvals[i]->v != v)
700 return NULL; /* wrong upvalue; cannot reuse closure */
701 }
702 p->cachemiss = 0; /* got a hit */
703 }
704 return c; /* return cached closure (or NULL if no cached closure) */
705}
706
707
708/*
709** create a new Lua closure, push it in the stack, and initialize 687** create a new Lua closure, push it in the stack, and initialize
710** its upvalues. ??? 688** its upvalues.
711*/ 689*/
712static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base, 690static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
713 StkId ra) { 691 StkId ra) {
@@ -724,13 +702,6 @@ static void pushclosure (lua_State *L, Proto *p, UpVal **encup, StkId base,
724 ncl->upvals[i] = encup[uv[i].idx]; 702 ncl->upvals[i] = encup[uv[i].idx];
725 luaC_objbarrier(L, ncl, ncl->upvals[i]); 703 luaC_objbarrier(L, ncl, ncl->upvals[i]);
726 } 704 }
727 if (p->cachemiss >= MAXMISS) /* too many missings? */
728 p->cache = NULL; /* give up cache */
729 else {
730 p->cache = ncl; /* save it on cache for reuse */
731 luaC_protobarrier(L, p, ncl);
732 p->cachemiss++;
733 }
734} 705}
735 706
736 707
@@ -1811,13 +1782,7 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1811 } 1782 }
1812 vmcase(OP_CLOSURE) { 1783 vmcase(OP_CLOSURE) {
1813 Proto *p = cl->p->p[GETARG_Bx(i)]; 1784 Proto *p = cl->p->p[GETARG_Bx(i)];
1814 LClosure *ncl = getcached(p, cl->upvals, base); /* cached closure */ 1785 halfProtect(pushclosure(L, p, cl->upvals, base, ra));
1815 if (ncl == NULL) { /* no match? */
1816 savestate(L, ci); /* in case of allocation errors */
1817 pushclosure(L, p, cl->upvals, base, ra); /* create a new one */
1818 }
1819 else
1820 setclLvalue2s(L, ra, ncl); /* push cashed closure */
1821 checkGC(L, ra + 1); 1786 checkGC(L, ra + 1);
1822 vmbreak; 1787 vmbreak;
1823 } 1788 }
diff --git a/testes/closure.lua b/testes/closure.lua
index 5d090d91..cdeaebaa 100644
--- a/testes/closure.lua
+++ b/testes/closure.lua
@@ -44,18 +44,17 @@ assert(B.g == 19)
44 44
45-- testing equality 45-- testing equality
46a = {} 46a = {}
47collectgarbage"stop"
48for i = 1, 5 do a[i] = function (x) return x + a + _ENV end end
49collectgarbage"restart"
50assert(a[3] == a[4] and a[4] == a[5])
51 47
52for i = 1, 5 do a[i] = function (x) return i + a + _ENV end end 48for i = 1, 5 do a[i] = function (x) return i + a + _ENV end end
53assert(a[3] ~= a[4] and a[4] ~= a[5]) 49assert(a[3] ~= a[4] and a[4] ~= a[5])
54 50
55local function f() 51do
56 return function (x) return math.sin(_ENV[x]) end 52 local a = function (x) return math.sin(_ENV[x]) end
53 local function f()
54 return a
55 end
56 assert(f() == f())
57end 57end
58assert(f() == f())
59 58
60 59
61-- testing closures with 'for' control variable 60-- testing closures with 'for' control variable