variable.c: use branch-free binary search for ivars

This commit optimizes instance variable lookups by replacing the
search algorithm with the same branch-free binary search recently
introduced for method lookups. This improves performance by
avoiding CPU branch mispredictions.
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-07-05 17:53:56 +09:00
parent 1167636998
commit e4b83f688e
+74 -68
View File
@@ -69,116 +69,122 @@ iv_rehash(mrb_state *mrb, iv_tbl *t)
mrb_free(mrb, old_ptr);
}
/* Set the value for the symbol in the instance variable table. */
/* Branch-free binary search helper: returns the index where `target` should be inserted/found. */
static inline int
bsearch_idx(mrb_sym *keys, int size, mrb_sym target) {
if (size == 0) return 0;
int n = size;
mrb_sym *p = keys;
/* While more than one element remains, halve the range each iteration */
while (n > 1) {
int half = n >> 1;
mrb_sym mid_sym = p[half];
/*
* Update pointer p without a branch:
* If mid_sym < target, move p forward by half; otherwise keep p unchanged.
* Compiler will emit a CMOV or equivalent.
*/
p = (mid_sym < target) ? p + half : p;
n -= half;
}
/* Final adjustment: if the remaining element is still less than target, advance by one */
return (int)(p - keys) + (keys[0] < target);
}
/* Set (insert or update) the value for `sym` in the instance variable table using branch-free search. */
static void
iv_put(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value val)
{
/* If table is uninitialized, allocate and initialize */
if (t->alloc == 0) {
iv_rehash(mrb, t);
}
/* Obtain pointers to keys and values arrays */
mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
mrb_value *vals = t->ptr;
mrb_value *vals = t->ptr;
/* 1) binary search for existing key */
int lo = 0, hi = t->size - 1;
while (lo <= hi) {
int mid = (lo + hi) >> 1;
mrb_sym k = keys[mid];
if (k == sym) {
vals[mid] = val; /* update */
return;
}
else if (k < sym) {
lo = mid + 1;
}
else {
hi = mid - 1;
}
/* Determine insertion/update index:
* If table has entries, use branch-free search; otherwise index = 0.
*/
int lo = bsearch_idx(keys, t->size, sym);
/* If the key already exists, update its value and return */
if (lo < t->size && keys[lo] == sym) {
vals[lo] = val;
return;
}
/* lo is insertion position */
/* 2) grow if full */
/* Grow table if full, then recompute position */
if (t->size == t->alloc) {
iv_rehash(mrb, t);
keys = (mrb_sym*)&t->ptr[t->alloc];
vals = t->ptr;
/* re-run binary search on the larger, sorted arrays */
lo = 0; hi = t->size - 1;
while (lo <= hi) {
int mid = (lo + hi) >> 1;
if (keys[mid] < sym) lo = mid + 1;
else hi = mid - 1;
}
lo = bsearch_idx(keys, t->size, sym);
}
/* 3) shift tail to make room */
int move = t->size - lo;
if (move > 0) {
memmove(&keys[lo+1], &keys[lo], sizeof(mrb_sym) * move);
memmove(&vals[lo+1], &vals[lo], sizeof(mrb_value) * move);
/* Shift existing entries right to make room at index lo */
int move_count = t->size - lo;
if (move_count > 0) {
memmove(&keys[lo + 1], &keys[lo], move_count * sizeof(mrb_sym));
memmove(&vals[lo + 1], &vals[lo], move_count * sizeof(mrb_value));
}
/* 4) insert new entry */
/* Insert the new key and value */
keys[lo] = sym;
vals[lo] = val;
t->size++;
}
/* Get a value for a symbol from the instance variable table. */
/* Get a value for `sym` from the instance variable table using branch-free search. */
static int
iv_get(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
/* Return 0 if table is null, uninitialized, or empty */
if (t == NULL || t->alloc == 0 || t->size == 0) return 0;
mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
mrb_value *vals = t->ptr;
int lo = 0, hi = t->size - 1;
while (lo <= hi) {
int mid = (lo + hi) >> 1;
mrb_sym k = keys[mid];
if (k == sym) {
if (vp) *vp = vals[mid];
return mid + 1;
}
else if (k < sym) {
lo = mid + 1;
}
else {
hi = mid - 1;
}
/* Find index in a branch-free manner */
int lo = bsearch_idx(keys, t->size, sym);
/* If found, store value (if vp provided) and return 1-based position */
if (lo < t->size && keys[lo] == sym) {
if (vp) *vp = vals[lo];
return lo + 1;
}
/* Not found */
return 0;
}
/* Deletes the value for the symbol from the instance variable table. */
/* Delete the entry for `sym` from the instance variable table using branch-free search. */
static mrb_bool
iv_del(mrb_state *mrb, iv_tbl *t, mrb_sym sym, mrb_value *vp)
{
/* Return FALSE if table is null, uninitialized, or empty */
if (t == NULL || t->alloc == 0 || t->size == 0) return FALSE;
mrb_sym *keys = (mrb_sym*)&t->ptr[t->alloc];
mrb_value *vals = t->ptr;
int lo = 0, hi = t->size - 1;
while (lo <= hi) {
int mid = (lo + hi) >> 1;
mrb_sym k = keys[mid];
if (k == sym) {
if (vp) *vp = vals[mid];
/* shift to remove */
int move = t->size - mid - 1;
if (move > 0) {
memmove(&keys[mid], &keys[mid+1], sizeof(mrb_sym) * move);
memmove(&vals[mid], &vals[mid+1], sizeof(mrb_value) * move);
}
t->size--;
return TRUE;
}
else if (k < sym) {
lo = mid + 1;
}
else {
hi = mid - 1;
/* Find index in a branch-free manner */
int lo = bsearch_idx(keys, t->size, sym);
/* If found, optionally return value and shift entries left to delete */
if (lo < t->size && keys[lo] == sym) {
if (vp) *vp = vals[lo];
int move_count = t->size - lo - 1;
if (move_count > 0) {
memmove(&keys[lo], &keys[lo + 1], move_count * sizeof(mrb_sym));
memmove(&vals[lo], &vals[lo + 1], move_count * sizeof(mrb_value));
}
t->size--;
return TRUE;
}
/* Not found */
return FALSE;
}