diff options
| author | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2022-11-01 15:42:08 -0300 |
|---|---|---|
| committer | Roberto Ierusalimschy <roberto@inf.puc-rio.br> | 2022-11-01 15:42:08 -0300 |
| commit | 8047b2d03eaaeee44871a11f8d3a3135f2639b1a (patch) | |
| tree | 2b2d246e2c63ba0accd7c3463a0d331669abe1ef | |
| parent | ee645472ebe153e2c6669c84a632297a8110bdb6 (diff) | |
| download | lua-8047b2d03eaaeee44871a11f8d3a3135f2639b1a.tar.gz lua-8047b2d03eaaeee44871a11f8d3a3135f2639b1a.tar.bz2 lua-8047b2d03eaaeee44871a11f8d3a3135f2639b1a.zip | |
Tables have a 'lastfree' information only when needed
Only tables with some minimum number of entries in their hash part
have a 'lastfree' field, kept in a header before the node vector.
| -rw-r--r-- | lmem.h | 2 | ||||
| -rw-r--r-- | lobject.h | 1 | ||||
| -rw-r--r-- | ltable.c | 80 | ||||
| -rw-r--r-- | ltable.h | 14 | ||||
| -rw-r--r-- | ltests.c | 3 | ||||
| -rw-r--r-- | ltm.h | 4 | ||||
| -rw-r--r-- | testes/nextvar.lua | 4 |
7 files changed, 84 insertions, 24 deletions
| @@ -63,6 +63,8 @@ | |||
| 63 | 63 | ||
| 64 | #define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) | 64 | #define luaM_newobject(L,tag,s) luaM_malloc_(L, (s), tag) |
| 65 | 65 | ||
| 66 | #define luaM_newblock(L, size) luaM_newvector(L, size, char) | ||
| 67 | |||
| 66 | #define luaM_growvector(L,v,nelems,size,t,limit,e) \ | 68 | #define luaM_growvector(L,v,nelems,size,t,limit,e) \ |
| 67 | ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ | 69 | ((v)=cast(t *, luaM_growaux_(L,v,nelems,&(size),sizeof(t), \ |
| 68 | luaM_limitN(limit,t),e))) | 70 | luaM_limitN(limit,t),e))) |
| @@ -744,7 +744,6 @@ typedef struct Table { | |||
| 744 | unsigned int alimit; /* "limit" of 'array' array */ | 744 | unsigned int alimit; /* "limit" of 'array' array */ |
| 745 | TValue *array; /* array part */ | 745 | TValue *array; /* array part */ |
| 746 | Node *node; | 746 | Node *node; |
| 747 | Node *lastfree; /* any free position is before this position */ | ||
| 748 | struct Table *metatable; | 747 | struct Table *metatable; |
| 749 | GCObject *gclist; | 748 | GCObject *gclist; |
| 750 | } Table; | 749 | } Table; |
| @@ -40,6 +40,27 @@ | |||
| 40 | 40 | ||
| 41 | 41 | ||
| 42 | /* | 42 | /* |
| 43 | ** Only tables with hash parts larget than LIMFORLAST has a 'lastfree' | ||
| 44 | ** field that optimizes finding a free slot. Smaller tables do a | ||
| 45 | ** complete search when looking for a free slot. | ||
| 46 | */ | ||
| 47 | #define LLIMFORLAST 2 /* log2 of LIMTFORLAST */ | ||
| 48 | #define LIMFORLAST twoto(LLIMFORLAST) | ||
| 49 | |||
| 50 | /* | ||
| 51 | ** Union to store an int field ensuring that what follows it in | ||
| 52 | ** memory is properly aligned to store a TValue. | ||
| 53 | */ | ||
| 54 | typedef union { | ||
| 55 | int lastfree; | ||
| 56 | char padding[offsetof(struct { int i; TValue v; }, v)]; | ||
| 57 | } Limbox; | ||
| 58 | |||
| 59 | #define haslastfree(t) ((t)->lsizenode > LLIMFORLAST) | ||
| 60 | #define getlastfree(t) (&((cast(Limbox *, (t)->node) - 1)->lastfree)) | ||
| 61 | |||
| 62 | |||
| 63 | /* | ||
| 43 | ** MAXABITS is the largest integer such that MAXASIZE fits in an | 64 | ** MAXABITS is the largest integer such that MAXASIZE fits in an |
| 44 | ** unsigned int. | 65 | ** unsigned int. |
| 45 | */ | 66 | */ |
| @@ -367,8 +388,15 @@ int luaH_next (lua_State *L, Table *t, StkId key) { | |||
| 367 | 388 | ||
| 368 | 389 | ||
| 369 | static void freehash (lua_State *L, Table *t) { | 390 | static void freehash (lua_State *L, Table *t) { |
| 370 | if (!isdummy(t)) | 391 | if (!isdummy(t)) { |
| 371 | luaM_freearray(L, t->node, cast_sizet(sizenode(t))); | 392 | size_t bsize = sizenode(t) * sizeof(Node); /* 'node' size in bytes */ |
| 393 | char *arr = cast_charp(t->node); | ||
| 394 | if (haslastfree(t)) { | ||
| 395 | bsize += sizeof(Limbox); | ||
| 396 | arr -= sizeof(Limbox); | ||
| 397 | } | ||
| 398 | luaM_freearray(L, arr, bsize); | ||
| 399 | } | ||
| 372 | } | 400 | } |
| 373 | 401 | ||
| 374 | 402 | ||
| @@ -479,7 +507,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { | |||
| 479 | if (size == 0) { /* no elements to hash part? */ | 507 | if (size == 0) { /* no elements to hash part? */ |
| 480 | t->node = cast(Node *, dummynode); /* use common 'dummynode' */ | 508 | t->node = cast(Node *, dummynode); /* use common 'dummynode' */ |
| 481 | t->lsizenode = 0; | 509 | t->lsizenode = 0; |
| 482 | t->lastfree = NULL; /* signal that it is using dummy node */ | 510 | setdummy(t); /* signal that it is using dummy node */ |
| 483 | } | 511 | } |
| 484 | else { | 512 | else { |
| 485 | int i; | 513 | int i; |
| @@ -487,15 +515,22 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) { | |||
| 487 | if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) | 515 | if (lsize > MAXHBITS || (1u << lsize) > MAXHSIZE) |
| 488 | luaG_runerror(L, "table overflow"); | 516 | luaG_runerror(L, "table overflow"); |
| 489 | size = twoto(lsize); | 517 | size = twoto(lsize); |
| 490 | t->node = luaM_newvector(L, size, Node); | 518 | if (lsize <= LLIMFORLAST) /* no 'lastfree' field? */ |
| 519 | t->node = luaM_newvector(L, size, Node); | ||
| 520 | else { | ||
| 521 | size_t bsize = size * sizeof(Node) + sizeof(Limbox); | ||
| 522 | char *node = luaM_newblock(L, bsize); | ||
| 523 | t->node = cast(Node *, node + sizeof(Limbox)); | ||
| 524 | *getlastfree(t) = size; /* all positions are free */ | ||
| 525 | } | ||
| 526 | t->lsizenode = cast_byte(lsize); | ||
| 527 | setnodummy(t); | ||
| 491 | for (i = 0; i < cast_int(size); i++) { | 528 | for (i = 0; i < cast_int(size); i++) { |
| 492 | Node *n = gnode(t, i); | 529 | Node *n = gnode(t, i); |
| 493 | gnext(n) = 0; | 530 | gnext(n) = 0; |
| 494 | setnilkey(n); | 531 | setnilkey(n); |
| 495 | setempty(gval(n)); | 532 | setempty(gval(n)); |
| 496 | } | 533 | } |
| 497 | t->lsizenode = cast_byte(lsize); | ||
| 498 | t->lastfree = gnode(t, size); /* all positions are free */ | ||
| 499 | } | 534 | } |
| 500 | } | 535 | } |
| 501 | 536 | ||
| @@ -520,18 +555,21 @@ static void reinsert (lua_State *L, Table *ot, Table *t) { | |||
| 520 | 555 | ||
| 521 | 556 | ||
| 522 | /* | 557 | /* |
| 523 | ** Exchange the hash part of 't1' and 't2'. | 558 | ** Exchange the hash part of 't1' and 't2'. (In 'flags', only the |
| 559 | ** dummy bit must be exchanged: The 'isrealasize' is not related | ||
| 560 | ** to the hash part, and the metamethod bits do not change during | ||
| 561 | ** a resize, so the "real" table can keep their values.) | ||
| 524 | */ | 562 | */ |
| 525 | static void exchangehashpart (Table *t1, Table *t2) { | 563 | static void exchangehashpart (Table *t1, Table *t2) { |
| 526 | lu_byte lsizenode = t1->lsizenode; | 564 | lu_byte lsizenode = t1->lsizenode; |
| 527 | Node *node = t1->node; | 565 | Node *node = t1->node; |
| 528 | Node *lastfree = t1->lastfree; | 566 | int bitdummy1 = t1->flags & BITDUMMY; |
| 529 | t1->lsizenode = t2->lsizenode; | 567 | t1->lsizenode = t2->lsizenode; |
| 530 | t1->node = t2->node; | 568 | t1->node = t2->node; |
| 531 | t1->lastfree = t2->lastfree; | 569 | t1->flags = (t1->flags & NOTBITDUMMY) | (t2->flags & BITDUMMY); |
| 532 | t2->lsizenode = lsizenode; | 570 | t2->lsizenode = lsizenode; |
| 533 | t2->node = node; | 571 | t2->node = node; |
| 534 | t2->lastfree = lastfree; | 572 | t2->flags = (t2->flags & NOTBITDUMMY) | bitdummy1; |
| 535 | } | 573 | } |
| 536 | 574 | ||
| 537 | 575 | ||
| @@ -555,6 +593,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize, | |||
| 555 | unsigned int oldasize = setlimittosize(t); | 593 | unsigned int oldasize = setlimittosize(t); |
| 556 | TValue *newarray; | 594 | TValue *newarray; |
| 557 | /* create new hash part with appropriate size into 'newt' */ | 595 | /* create new hash part with appropriate size into 'newt' */ |
| 596 | newt.flags = 0; | ||
| 558 | setnodevector(L, &newt, nhsize); | 597 | setnodevector(L, &newt, nhsize); |
| 559 | if (newasize < oldasize) { /* will array shrink? */ | 598 | if (newasize < oldasize) { /* will array shrink? */ |
| 560 | t->alimit = newasize; /* pretend array has new size... */ | 599 | t->alimit = newasize; /* pretend array has new size... */ |
| @@ -641,11 +680,22 @@ void luaH_free (lua_State *L, Table *t) { | |||
| 641 | 680 | ||
| 642 | 681 | ||
| 643 | static Node *getfreepos (Table *t) { | 682 | static Node *getfreepos (Table *t) { |
| 644 | if (!isdummy(t)) { | 683 | if (haslastfree(t)) { /* does it have 'lastfree' information? */ |
| 645 | while (t->lastfree > t->node) { | 684 | /* look for a spot before 'lastfree', updating 'lastfree' */ |
| 646 | t->lastfree--; | 685 | while (*getlastfree(t) > 0) { |
| 647 | if (keyisnil(t->lastfree)) | 686 | Node *free = gnode(t, --(*getlastfree(t))); |
| 648 | return t->lastfree; | 687 | if (keyisnil(free)) |
| 688 | return free; | ||
| 689 | } | ||
| 690 | } | ||
| 691 | else { /* no 'lastfree' information */ | ||
| 692 | if (!isdummy(t)) { | ||
| 693 | int i = sizenode(t); | ||
| 694 | while (i--) { /* do a linear search */ | ||
| 695 | Node *free = gnode(t, i); | ||
| 696 | if (keyisnil(free)) | ||
| 697 | return free; | ||
| 698 | } | ||
| 649 | } | 699 | } |
| 650 | } | 700 | } |
| 651 | return NULL; /* could not find a free place */ | 701 | return NULL; /* could not find a free place */ |
| @@ -23,8 +23,18 @@ | |||
| 23 | #define invalidateTMcache(t) ((t)->flags &= ~maskflags) | 23 | #define invalidateTMcache(t) ((t)->flags &= ~maskflags) |
| 24 | 24 | ||
| 25 | 25 | ||
| 26 | /* true when 't' is using 'dummynode' as its hash part */ | 26 | /* |
| 27 | #define isdummy(t) ((t)->lastfree == NULL) | 27 | ** Bit BITDUMMY set in 'flags' means the table is using the dummy node |
| 28 | ** for its hash part. | ||
| 29 | */ | ||
| 30 | |||
| 31 | #define BITDUMMY (1 << 6) | ||
| 32 | #define NOTBITDUMMY cast_byte(~BITDUMMY) | ||
| 33 | #define isdummy(t) ((t)->flags & BITDUMMY) | ||
| 34 | |||
| 35 | #define setnodummy(t) ((t)->flags &= NOTBITDUMMY) | ||
| 36 | #define setdummy(t) ((t)->flags |= BITDUMMY) | ||
| 37 | |||
| 28 | 38 | ||
| 29 | 39 | ||
| 30 | /* allocated size for hash nodes */ | 40 | /* allocated size for hash nodes */ |
| @@ -999,9 +999,8 @@ static int table_query (lua_State *L) { | |||
| 999 | if (i == -1) { | 999 | if (i == -1) { |
| 1000 | lua_pushinteger(L, asize); | 1000 | lua_pushinteger(L, asize); |
| 1001 | lua_pushinteger(L, allocsizenode(t)); | 1001 | lua_pushinteger(L, allocsizenode(t)); |
| 1002 | lua_pushinteger(L, isdummy(t) ? 0 : t->lastfree - t->node); | ||
| 1003 | lua_pushinteger(L, t->alimit); | 1002 | lua_pushinteger(L, t->alimit); |
| 1004 | return 4; | 1003 | return 3; |
| 1005 | } | 1004 | } |
| 1006 | else if ((unsigned int)i < asize) { | 1005 | else if ((unsigned int)i < asize) { |
| 1007 | lua_pushinteger(L, i); | 1006 | lua_pushinteger(L, i); |
| @@ -48,8 +48,8 @@ typedef enum { | |||
| 48 | /* | 48 | /* |
| 49 | ** Mask with 1 in all fast-access methods. A 1 in any of these bits | 49 | ** Mask with 1 in all fast-access methods. A 1 in any of these bits |
| 50 | ** in the flag of a (meta)table means the metatable does not have the | 50 | ** in the flag of a (meta)table means the metatable does not have the |
| 51 | ** corresponding metamethod field. (Bit 7 of the flag is used for | 51 | ** corresponding metamethod field. (Bit 6 of the flag indicates that |
| 52 | ** 'isrealasize'.) | 52 | ** the table is using the dummy node; bit 7 is used for 'isrealasize'.) |
| 53 | */ | 53 | */ |
| 54 | #define maskflags (~(~0u << (TM_EQ + 1))) | 54 | #define maskflags (~(~0u << (TM_EQ + 1))) |
| 55 | 55 | ||
diff --git a/testes/nextvar.lua b/testes/nextvar.lua index 0874e5bb..80b3d05c 100644 --- a/testes/nextvar.lua +++ b/testes/nextvar.lua | |||
| @@ -210,9 +210,9 @@ assert(T.querytab(a) == 64) -- array part has 64 elements | |||
| 210 | a[32] = true; a[48] = true; -- binary search will find these ones | 210 | a[32] = true; a[48] = true; -- binary search will find these ones |
| 211 | a[51] = true -- binary search will miss this one | 211 | a[51] = true -- binary search will miss this one |
| 212 | assert(#a == 48) -- this will set the limit | 212 | assert(#a == 48) -- this will set the limit |
| 213 | assert(select(4, T.querytab(a)) == 48) -- this is the limit now | 213 | assert(select(3, T.querytab(a)) == 48) -- this is the limit now |
| 214 | a[50] = true -- this will set a new limit | 214 | a[50] = true -- this will set a new limit |
| 215 | assert(select(4, T.querytab(a)) == 50) -- this is the limit now | 215 | assert(select(3, T.querytab(a)) == 50) -- this is the limit now |
| 216 | -- but the size is larger (and still inside the array part) | 216 | -- but the size is larger (and still inside the array part) |
| 217 | assert(#a == 51) | 217 | assert(#a == 51) |
| 218 | 218 | ||
