diff options
author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-08-12 15:32:44 -0300 |
---|---|---|
committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2019-08-12 15:32:44 -0300 |
commit | f64a1b175a5fa65434a073e6d071b32bb7b0ab69 (patch) | |
tree | dafd99aab55c4508d88906a17e63582ce7187daf /lgc.c | |
parent | 09b4e527a008687a2fd90202b5e7b2f175d8ebed (diff) | |
download | lua-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.c | 29 |
1 files changed, 20 insertions, 9 deletions
@@ -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 | */ |
416 | static int traverseephemeron (global_State *g, Table *h) { | 416 | static 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 | */ | ||
623 | static void convergeephemerons (global_State *g) { | 632 | static 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 | /* }====================================================== */ |