aboutsummaryrefslogtreecommitdiff
path: root/lgc.c
diff options
context:
space:
mode:
authorRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-08-12 15:32:44 -0300
committerRoberto Ierusalimschy <roberto@inf.puc-rio.br>2019-08-12 15:32:44 -0300
commitf64a1b175a5fa65434a073e6d071b32bb7b0ab69 (patch)
treedafd99aab55c4508d88906a17e63582ce7187daf /lgc.c
parent09b4e527a008687a2fd90202b5e7b2f175d8ebed (diff)
downloadlua-f64a1b175a5fa65434a073e6d071b32bb7b0ab69.tar.gz
lua-f64a1b175a5fa65434a073e6d071b32bb7b0ab69.tar.bz2
lua-f64a1b175a5fa65434a073e6d071b32bb7b0ab69.zip
Small optimization in 'convergeephemerons'
When converging marks on ephemeron tables, change the direction the tables are traversed at each iteration, to try to avoid bad-case scenarios with linked lists of entries in a table.
Diffstat (limited to 'lgc.c')
-rw-r--r--lgc.c29
1 files changed, 20 insertions, 9 deletions
diff --git a/lgc.c b/lgc.c
index b7220cf1..75670c0a 100644
--- a/lgc.c
+++ b/lgc.c
@@ -413,13 +413,13 @@ static void traverseweakvalue (global_State *g, Table *h) {
413** (in the atomic phase). In generational mode, it (like all visited 413** (in the atomic phase). In generational mode, it (like all visited
414** tables) must be kept in some gray list for post-processing. 414** tables) must be kept in some gray list for post-processing.
415*/ 415*/
416static int traverseephemeron (global_State *g, Table *h) { 416static int traverseephemeron (global_State *g, Table *h, int inv) {
417 int marked = 0; /* true if an object is marked in this traversal */ 417 int marked = 0; /* true if an object is marked in this traversal */
418 int hasclears = 0; /* true if table has white keys */ 418 int hasclears = 0; /* true if table has white keys */
419 int hasww = 0; /* true if table has entry "white-key -> white-value" */ 419 int hasww = 0; /* true if table has entry "white-key -> white-value" */
420 Node *n, *limit = gnodelast(h);
421 unsigned int i; 420 unsigned int i;
422 unsigned int asize = luaH_realasize(h); 421 unsigned int asize = luaH_realasize(h);
422 unsigned int nsize = sizenode(h);
423 /* traverse array part */ 423 /* traverse array part */
424 for (i = 0; i < asize; i++) { 424 for (i = 0; i < asize; i++) {
425 if (valiswhite(&h->array[i])) { 425 if (valiswhite(&h->array[i])) {
@@ -427,8 +427,10 @@ static int traverseephemeron (global_State *g, Table *h) {
427 reallymarkobject(g, gcvalue(&h->array[i])); 427 reallymarkobject(g, gcvalue(&h->array[i]));
428 } 428 }
429 } 429 }
430 /* traverse hash part */ 430 /* traverse hash part; if 'inv', traverse descending
431 for (n = gnode(h, 0); n < limit; n++) { 431 (see 'convergeephemerons') */
432 for (i = 0; i < nsize; i++) {
433 Node *n = inv ? gnode(h, nsize - 1 - i) : gnode(h, i);
432 if (isempty(gval(n))) /* entry is empty? */ 434 if (isempty(gval(n))) /* entry is empty? */
433 clearkey(n); /* clear its key */ 435 clearkey(n); /* clear its key */
434 else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */ 436 else if (iscleared(g, gckeyN(n))) { /* key is not marked (yet)? */
@@ -490,7 +492,7 @@ static lu_mem traversetable (global_State *g, Table *h) {
490 if (!weakkey) /* strong keys? */ 492 if (!weakkey) /* strong keys? */
491 traverseweakvalue(g, h); 493 traverseweakvalue(g, h);
492 else if (!weakvalue) /* strong values? */ 494 else if (!weakvalue) /* strong values? */
493 traverseephemeron(g, h); 495 traverseephemeron(g, h, 0);
494 else /* all weak */ 496 else /* all weak */
495 linkgclist(h, g->allweak); /* nothing to traverse now */ 497 linkgclist(h, g->allweak); /* nothing to traverse now */
496 } 498 }
@@ -620,21 +622,30 @@ static lu_mem propagateall (global_State *g) {
620} 622}
621 623
622 624
625/*
626** Traverse all ephemeron tables propagating marks from keys to values.
627** Repeat until it converges, that is, nothing new is marked. 'dir'
628** inverts the direction of the traversals, trying to speed up
629** convergence on chains in the same table.
630**
631*/
623static void convergeephemerons (global_State *g) { 632static void convergeephemerons (global_State *g) {
624 int changed; 633 int changed;
634 int dir = 0;
625 do { 635 do {
626 GCObject *w; 636 GCObject *w;
627 GCObject *next = g->ephemeron; /* get ephemeron list */ 637 GCObject *next = g->ephemeron; /* get ephemeron list */
628 g->ephemeron = NULL; /* tables may return to this list when traversed */ 638 g->ephemeron = NULL; /* tables may return to this list when traversed */
629 changed = 0; 639 changed = 0;
630 while ((w = next) != NULL) { 640 while ((w = next) != NULL) { /* for each ephemeron table */
631 next = gco2t(w)->gclist; 641 next = gco2t(w)->gclist; /* list is rebuilt during loop */
632 if (traverseephemeron(g, gco2t(w))) { /* traverse marked some value? */ 642 if (traverseephemeron(g, gco2t(w), dir)) { /* marked some value? */
633 propagateall(g); /* propagate changes */ 643 propagateall(g); /* propagate changes */
634 changed = 1; /* will have to revisit all ephemeron tables */ 644 changed = 1; /* will have to revisit all ephemeron tables */
635 } 645 }
636 } 646 }
637 } while (changed); 647 dir = !dir; /* invert direction next time */
648 } while (changed); /* repeat until no more changes */
638} 649}
639 650
640/* }====================================================== */ 651/* }====================================================== */