1
0
mirror of https://github.com/lua/lua.git synced 2025-01-28 06:03:00 +08:00

New structure to count keys in a table for rehashing

This commit is contained in:
Roberto Ierusalimschy 2024-10-28 14:15:21 -03:00
parent 853311e5b1
commit 0de8191152

115
ltable.c
View File

@ -456,15 +456,29 @@ static int keyinarray (Table *t, lua_Integer key) {
** ============================================================== ** ==============================================================
*/ */
/* /*
** Compute the optimal size for the array part of table 't'. 'nums' is a ** Structure to count the keys in a table.
** "count array" where 'nums[i]' is the number of integers in the table ** 'total' is the total number of keys in the table.
** between 2^(i - 1) + 1 and 2^i. 'pna' enters with the total number of ** 'na' is the number of *array indices* in the table (see 'arrayindex').
** integer keys in the table and leaves with the number of keys that ** 'nums' is a "count array" where 'nums[i]' is the number of integer
** will go to the array part; return the optimal size. (The condition ** keys between 2^(i - 1) + 1 and 2^i. Note that 'na' is the summation
** 'twotoi > 0' in the for loop stops the loop if 'twotoi' overflows.) ** of 'nums'.
*/ */
static unsigned computesizes (unsigned nums[], unsigned *pna) { typedef struct {
unsigned total;
unsigned na;
unsigned nums[MAXABITS + 1];
} Counters;
/*
** Compute the optimal size for the array part of table 't'.
** 'ct->na' enters with the total number of array indices in the table
** and leaves with the number of keys that will go to the array part;
** return the optimal size. (The condition 'twotoi > 0' in the for loop
** stops the loop if 'twotoi' overflows.)
*/
static unsigned computesizes (Counters *ct) {
int i; int i;
unsigned int twotoi; /* 2^i (candidate for optimal size) */ unsigned int twotoi; /* 2^i (candidate for optimal size) */
unsigned int a = 0; /* number of elements smaller than 2^i */ unsigned int a = 0; /* number of elements smaller than 2^i */
@ -472,28 +486,26 @@ static unsigned computesizes (unsigned nums[], unsigned *pna) {
unsigned int optimal = 0; /* optimal size for array part */ unsigned int optimal = 0; /* optimal size for array part */
/* loop while keys can fill more than half of total size */ /* loop while keys can fill more than half of total size */
for (i = 0, twotoi = 1; for (i = 0, twotoi = 1;
twotoi > 0 && *pna > twotoi / 2; twotoi > 0 && ct->na > twotoi / 2;
i++, twotoi *= 2) { i++, twotoi *= 2) {
a += nums[i]; a += ct->nums[i];
if (a > twotoi/2) { /* more than half elements present? */ if (a > twotoi/2) { /* more than half elements present? */
optimal = twotoi; /* optimal size (till now) */ optimal = twotoi; /* optimal size (till now) */
na = a; /* all elements up to 'optimal' will go to array part */ na = a; /* all elements up to 'optimal' will go to array part */
} }
} }
lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal); lua_assert((optimal == 0 || optimal / 2 < na) && na <= optimal);
*pna = na; ct->na = na;
return optimal; return optimal;
} }
static unsigned countint (lua_Integer key, unsigned int *nums) { static void countint (lua_Integer key, Counters *ct) {
unsigned int k = arrayindex(key); unsigned int k = arrayindex(key);
if (k != 0) { /* is 'key' an appropriate array index? */ if (k != 0) { /* is 'key' an array index? */
nums[luaO_ceillog2(k)]++; /* count as such */ ct->nums[luaO_ceillog2(k)]++; /* count as such */
return 1; ct->na++;
} }
else
return 0;
} }
@ -504,11 +516,9 @@ l_sinline int arraykeyisempty (const Table *t, lua_Unsigned key) {
/* /*
** Count keys in array part of table 't': Fill 'nums[i]' with ** Count keys in array part of table 't'.
** number of keys that will go into corresponding slice and return
** total number of non-nil keys.
*/ */
static unsigned numusearray (const Table *t, unsigned *nums) { static void numusearray (const Table *t, Counters *ct) {
int lg; int lg;
unsigned int ttlg; /* 2^lg */ unsigned int ttlg; /* 2^lg */
unsigned int ause = 0; /* summation of 'nums' */ unsigned int ause = 0; /* summation of 'nums' */
@ -528,27 +538,29 @@ static unsigned numusearray (const Table *t, unsigned *nums) {
if (!arraykeyisempty(t, i)) if (!arraykeyisempty(t, i))
lc++; lc++;
} }
nums[lg] += lc; ct->nums[lg] += lc;
ause += lc; ause += lc;
} }
return ause; ct->total += ause;
ct->na += ause;
} }
static unsigned numusehash (const Table *t, unsigned *nums, unsigned *pna) { /*
unsigned totaluse = 0; /* total number of elements */ ** Count keys in hash part of table 't'.
unsigned ause = 0; /* elements added to 'nums' (can go to array part) */ */
static void numusehash (const Table *t, Counters *ct) {
unsigned i = sizenode(t); unsigned i = sizenode(t);
unsigned total = 0;
while (i--) { while (i--) {
Node *n = &t->node[i]; Node *n = &t->node[i];
if (!isempty(gval(n))) { if (!isempty(gval(n))) {
total++;
if (keyisinteger(n)) if (keyisinteger(n))
ause += countint(keyival(n), nums); countint(keyival(n), ct);
totaluse++;
} }
} }
*pna += ause; ct->total += total;
return totaluse;
} }
@ -566,12 +578,11 @@ static size_t concretesize (unsigned int size) {
** do nothing. Else, if new size is zero, free the old array. (It must ** do nothing. Else, if new size is zero, free the old array. (It must
** be present, as the sizes are different.) Otherwise, allocate a new ** be present, as the sizes are different.) Otherwise, allocate a new
** array, move the common elements to new proper position, and then ** array, move the common elements to new proper position, and then
** frees old array. ** frees the old array.
** When array grows, we could reallocate it, but we still would need ** We could reallocate the array, but we still would need to move the
** to move the elements to their new position, so the copy implicit ** elements to their new position, so the copy implicit in realloc is a
** in realloc is a waste. When array shrinks, it always erases some ** waste. Moreover, most allocators will move the array anyway when the
** elements that should still be in the array, so we must reallocate in ** new size is double the old one (the most common case).
** two steps anyway. It is simpler to always reallocate in two steps.
*/ */
static Value *resizearray (lua_State *L , Table *t, static Value *resizearray (lua_State *L , Table *t,
unsigned oldasize, unsigned oldasize,
@ -590,10 +601,10 @@ static Value *resizearray (lua_State *L , Table *t,
if (np == NULL) /* allocation error? */ if (np == NULL) /* allocation error? */
return NULL; return NULL;
if (oldasize > 0) { if (oldasize > 0) {
/* move common elements to new position */
Value *op = t->array - oldasize; /* real original array */ Value *op = t->array - oldasize; /* real original array */
unsigned tomove = (oldasize < newasize) ? oldasize : newasize; unsigned tomove = (oldasize < newasize) ? oldasize : newasize;
lua_assert(tomove > 0); lua_assert(tomove > 0);
/* move common elements to new position */
memcpy(np + newasize - tomove, memcpy(np + newasize - tomove,
op + oldasize - tomove, op + oldasize - tomove,
concretesize(tomove)); concretesize(tomove));
@ -723,6 +734,9 @@ static void clearNewSlice (Table *t, unsigned oldasize, unsigned newasize) {
** into the table, initializes the new part of the array (if any) with ** into the table, initializes the new part of the array (if any) with
** nils and reinserts the elements of the old hash back into the new ** nils and reinserts the elements of the old hash back into the new
** parts of the table. ** parts of the table.
** Note that if the new size for the arry part ('newasize') is equal to
** the old one ('oldasize'), this function will do nothing with that
** part.
*/ */
void luaH_resize (lua_State *L, Table *t, unsigned newasize, void luaH_resize (lua_State *L, Table *t, unsigned newasize,
unsigned nhsize) { unsigned nhsize) {
@ -762,33 +776,34 @@ void luaH_resizearray (lua_State *L, Table *t, unsigned int nasize) {
luaH_resize(L, t, nasize, nsize); luaH_resize(L, t, nasize, nsize);
} }
/* /*
** nums[i] = number of keys 'k' where 2^(i - 1) < k <= 2^i ** Rehash a table. First, count its keys. If there are array indices
** outside the array part, compute the new best size for that part.
** Then, resize the table.
*/ */
static void rehash (lua_State *L, Table *t, const TValue *ek) { static void rehash (lua_State *L, Table *t, const TValue *ek) {
unsigned asize; /* optimal size for array part */ unsigned asize; /* optimal size for array part */
unsigned na = 0; /* number of keys candidate for the array part */ Counters ct;
unsigned nums[MAXABITS + 1];
unsigned i; unsigned i;
unsigned totaluse; /* total number of keys */
for (i = 0; i <= MAXABITS; i++) nums[i] = 0; /* reset counts */
setlimittosize(t); setlimittosize(t);
totaluse = 1; /* count extra key */ /* reset counts */
for (i = 0; i <= MAXABITS; i++) ct.nums[i] = 0;
ct.na = 0;
ct.total = 1; /* count extra key */
if (ttisinteger(ek)) if (ttisinteger(ek))
na += countint(ivalue(ek), nums); /* extra key may go to array */ countint(ivalue(ek), &ct); /* extra key may go to array */
totaluse += numusehash(t, nums, &na); /* count keys in hash part */ numusehash(t, &ct); /* count keys in hash part */
if (na == 0) { if (ct.na == 0) {
/* no new keys to enter array part; keep it with the same size */ /* no new keys to enter array part; keep it with the same size */
asize = luaH_realasize(t); asize = luaH_realasize(t);
} }
else { /* compute best size for array part */ else { /* compute best size for array part */
unsigned n = numusearray(t, nums); /* count keys in array part */ numusearray(t, &ct); /* count keys in array part */
totaluse += n; /* all keys in array part are keys */ asize = computesizes(&ct); /* compute new size for array part */
na += n; /* all keys in array part are candidates for new array part */
asize = computesizes(nums, &na); /* compute new size for array part */
} }
/* resize the table to new computed sizes */ /* resize the table to new computed sizes */
luaH_resize(L, t, asize, totaluse - na); luaH_resize(L, t, asize, ct.total - ct.na);
} }