mirror of
https://github.com/DoneJS-Runtime/quickjs-done-nextgen.git
synced 2025-01-09 17:43:15 +00:00
Improve performance of variable resolver (#672)
Switch to a hash table when the number of variables grows beyond a threshold. Speeds up the test case from the linked issue by about 70%. Fixes: https://github.com/quickjs-ng/quickjs/issues/456
This commit is contained in:
parent
0362c0a4eb
commit
000061f635
1 changed files with 100 additions and 2 deletions
102
quickjs.c
102
quickjs.c
|
@ -18695,6 +18695,7 @@ typedef struct JSFunctionDef {
|
||||||
JSAtom func_name; /* JS_ATOM_NULL if no name */
|
JSAtom func_name; /* JS_ATOM_NULL if no name */
|
||||||
|
|
||||||
JSVarDef *vars;
|
JSVarDef *vars;
|
||||||
|
uint32_t *vars_htab; // indexes into vars[]
|
||||||
int var_size; /* allocated size for vars[] */
|
int var_size; /* allocated size for vars[] */
|
||||||
int var_count;
|
int var_count;
|
||||||
JSVarDef *args;
|
JSVarDef *args;
|
||||||
|
@ -20517,6 +20518,86 @@ static __exception int emit_push_const(JSParseState *s, JSValue val,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// perl hash; variation of k&r hash with a different magic multiplier
|
||||||
|
// and a final shuffle to improve distribution of the low-order bits
|
||||||
|
static uint32_t hash_bytes(uint32_t h, const void *b, size_t n)
|
||||||
|
{
|
||||||
|
const char *p;
|
||||||
|
|
||||||
|
for (p = b; p < (char *)b + n; p++)
|
||||||
|
h = 33*h + *p;
|
||||||
|
h += h >> 5;
|
||||||
|
return h;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t hash_atom(JSAtom atom)
|
||||||
|
{
|
||||||
|
return hash_bytes(0, &atom, sizeof(atom));
|
||||||
|
}
|
||||||
|
|
||||||
|
// caveat emptor: the table size must be a power of two in order for
|
||||||
|
// masking to work, and the load factor constant must be an odd number (5)
|
||||||
|
//
|
||||||
|
// f(n)=n+n/t is used to estimate the load factor but changing t to an
|
||||||
|
// even number introduces gaps in the output of f, sometimes "jumping"
|
||||||
|
// over the next power of two; it's at powers of two when the hash table
|
||||||
|
// must be resized
|
||||||
|
static int update_var_htab(JSContext *ctx, JSFunctionDef *fd)
|
||||||
|
{
|
||||||
|
uint32_t i, j, k, m, *p;
|
||||||
|
|
||||||
|
if (fd->var_count < 27) // 27 + 27/5 == 32
|
||||||
|
return 0;
|
||||||
|
k = fd->var_count - 1;
|
||||||
|
m = fd->var_count + fd->var_count/5;
|
||||||
|
if (m & (m - 1)) // unless power of two
|
||||||
|
goto insert;
|
||||||
|
m *= 2;
|
||||||
|
p = js_realloc(ctx, fd->vars_htab, m * sizeof(*fd->vars_htab));
|
||||||
|
if (!p)
|
||||||
|
return -1;
|
||||||
|
for (i = 0; i < m; i++)
|
||||||
|
p[i] = UINT32_MAX;
|
||||||
|
fd->vars_htab = p;
|
||||||
|
k = 0;
|
||||||
|
m--;
|
||||||
|
insert:
|
||||||
|
m = UINT32_MAX >> clz32(m);
|
||||||
|
do {
|
||||||
|
i = hash_atom(fd->vars[k].var_name);
|
||||||
|
j = 1;
|
||||||
|
for (;;) {
|
||||||
|
p = &fd->vars_htab[i & m];
|
||||||
|
if (*p == UINT32_MAX)
|
||||||
|
break;
|
||||||
|
i += j;
|
||||||
|
j += 1; // quadratic probing
|
||||||
|
}
|
||||||
|
*p = k++;
|
||||||
|
} while (k < (uint32_t)fd->var_count);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int find_var_htab(JSFunctionDef *fd, JSAtom var_name)
|
||||||
|
{
|
||||||
|
uint32_t i, j, m, *p;
|
||||||
|
|
||||||
|
i = hash_atom(var_name);
|
||||||
|
j = 1;
|
||||||
|
m = fd->var_count + fd->var_count/5;
|
||||||
|
m = UINT32_MAX >> clz32(m);
|
||||||
|
for (;;) {
|
||||||
|
p = &fd->vars_htab[i & m];
|
||||||
|
if (*p == UINT32_MAX)
|
||||||
|
return -1;
|
||||||
|
if (fd->vars[*p].var_name == var_name)
|
||||||
|
return *p;
|
||||||
|
i += j;
|
||||||
|
j += 1; // quadratic probing
|
||||||
|
}
|
||||||
|
return -1; // pacify compiler
|
||||||
|
}
|
||||||
|
|
||||||
/* return the variable index or -1 if not found,
|
/* return the variable index or -1 if not found,
|
||||||
add ARGUMENT_VAR_OFFSET for argument variables */
|
add ARGUMENT_VAR_OFFSET for argument variables */
|
||||||
static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
|
static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
|
||||||
|
@ -20531,11 +20612,24 @@ static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
|
||||||
|
|
||||||
static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
|
static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
|
||||||
{
|
{
|
||||||
|
JSVarDef *vd;
|
||||||
int i;
|
int i;
|
||||||
for(i = fd->var_count; i-- > 0;) {
|
|
||||||
if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
|
if (fd->vars_htab) {
|
||||||
|
i = find_var_htab(fd, name);
|
||||||
|
if (i == -1)
|
||||||
|
goto not_found;
|
||||||
|
vd = &fd->vars[i];
|
||||||
|
if (fd->vars[i].scope_level == 0)
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
for(i = fd->var_count; i-- > 0;) {
|
||||||
|
vd = &fd->vars[i];
|
||||||
|
if (vd->var_name == name)
|
||||||
|
if (vd->scope_level == 0)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
not_found:
|
||||||
return find_arg(ctx, fd, name);
|
return find_arg(ctx, fd, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20710,6 +20804,8 @@ static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
|
||||||
memset(vd, 0, sizeof(*vd));
|
memset(vd, 0, sizeof(*vd));
|
||||||
vd->var_name = JS_DupAtom(ctx, name);
|
vd->var_name = JS_DupAtom(ctx, name);
|
||||||
vd->func_pool_idx = -1;
|
vd->func_pool_idx = -1;
|
||||||
|
if (update_var_htab(ctx, fd))
|
||||||
|
return -1;
|
||||||
return fd->var_count - 1;
|
return fd->var_count - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28410,6 +28506,7 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
|
||||||
JS_FreeAtom(ctx, fd->vars[i].var_name);
|
JS_FreeAtom(ctx, fd->vars[i].var_name);
|
||||||
}
|
}
|
||||||
js_free(ctx, fd->vars);
|
js_free(ctx, fd->vars);
|
||||||
|
js_free(ctx, fd->vars_htab); // XXX can probably be freed earlier?
|
||||||
for(i = 0; i < fd->arg_count; i++) {
|
for(i = 0; i < fd->arg_count; i++) {
|
||||||
JS_FreeAtom(ctx, fd->args[i].var_name);
|
JS_FreeAtom(ctx, fd->args[i].var_name);
|
||||||
}
|
}
|
||||||
|
@ -32258,6 +32355,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
|
||||||
b->defined_arg_count = fd->defined_arg_count;
|
b->defined_arg_count = fd->defined_arg_count;
|
||||||
js_free(ctx, fd->args);
|
js_free(ctx, fd->args);
|
||||||
js_free(ctx, fd->vars);
|
js_free(ctx, fd->vars);
|
||||||
|
js_free(ctx, fd->vars_htab);
|
||||||
}
|
}
|
||||||
b->cpool_count = fd->cpool_count;
|
b->cpool_count = fd->cpool_count;
|
||||||
if (b->cpool_count) {
|
if (b->cpool_count) {
|
||||||
|
|
Loading…
Reference in a new issue