mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
class.c: use linear search for method tables; make ROM entries const
Replace binary search with linear scan in mt_get(), mt_put(), mt_del(), mt_chain_has(), and mrb_mt_foreach(). The method cache makes repeated lookups O(1), so linear scan on cache misses is acceptable. This removes the sorting requirement, allowing ROM entry arrays to be declared const. On embedded systems, const static data resides in flash/ROM instead of RAM, saving ~8.4KB for ~700 method entries on 32-bit MCUs. Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -2249,7 +2249,7 @@ mrb_ary_to_a(mrb_state *mrb, mrb_value self)
|
||||
}
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry array_rom_entries[] = {
|
||||
static const mrb_mt_entry array_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_ary_plus, MRB_OPSYM(add), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_ary_times, MRB_OPSYM(mul), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_ary_push_m, MRB_OPSYM(lshift), MRB_MT_FUNC),
|
||||
|
||||
+45
-105
@@ -53,85 +53,48 @@ mt_new(mrb_state *mrb)
|
||||
return t;
|
||||
}
|
||||
|
||||
/* Branch-free binary search helper for method table entries */
|
||||
static inline int
|
||||
mt_bsearch_idx(mrb_mt_entry *entries, int size, mrb_sym target)
|
||||
{
|
||||
if (size == 0) return 0;
|
||||
int n = size;
|
||||
mrb_mt_entry *p = entries;
|
||||
/* While more than one element remains, halve the range each iteration */
|
||||
while (n > 1) {
|
||||
int half = n >> 1;
|
||||
MRB_MEM_PREFETCH(p + (half >> 1));
|
||||
MRB_MEM_PREFETCH(p + half + (half >> 1));
|
||||
/*
|
||||
* Update pointer p without a branch:
|
||||
* If key < target, move p forward by half; otherwise keep p unchanged.
|
||||
* Compiler will emit a CMOV or equivalent.
|
||||
*/
|
||||
p = (p[half].key < target) ? p + half : p;
|
||||
n -= half;
|
||||
}
|
||||
/* Final adjustment: if the remaining element is still less than target, advance by one */
|
||||
int offset = (p->key < target);
|
||||
return (int)(p - entries) + offset;
|
||||
}
|
||||
|
||||
/* Inserts or updates an entry in the method table */
|
||||
/* Inserts or updates an entry in the method table (linear scan) */
|
||||
static void
|
||||
mt_put(mrb_state *mrb, mrb_mt_tbl *t, mrb_sym sym, uint32_t flags, union mrb_mt_ptr ptrval)
|
||||
{
|
||||
/* Ensure there is capacity */
|
||||
mrb_mt_entry *entries = t->ptr;
|
||||
|
||||
/* Linear scan for existing key */
|
||||
for (int i = 0; i < t->size; i++) {
|
||||
if (entries[i].key == sym) {
|
||||
entries[i].flags = flags;
|
||||
entries[i].val = ptrval;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Not found — append to end */
|
||||
if (MT_ALLOC(t) == 0) {
|
||||
mt_grow(mrb, t, 8);
|
||||
}
|
||||
else if (t->size == MT_ALLOC(t)) {
|
||||
mt_grow(mrb, t, MT_ALLOC(t) * 2);
|
||||
}
|
||||
|
||||
mrb_mt_entry *entries = t->ptr;
|
||||
|
||||
/*
|
||||
* If table is empty, insertion index is 0.
|
||||
* Otherwise, find the insertion/update position branch-free.
|
||||
*/
|
||||
int lo = mt_bsearch_idx(entries, t->size, sym);
|
||||
|
||||
/* If the key already exists, update its value and return */
|
||||
if (lo < t->size && entries[lo].key == sym) {
|
||||
entries[lo].flags = flags;
|
||||
entries[lo].val = ptrval;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Shift existing entries to make room at index lo */
|
||||
if (t->size > lo) {
|
||||
memmove(&entries[lo+1], &entries[lo],
|
||||
(t->size - lo) * sizeof(mrb_mt_entry));
|
||||
}
|
||||
|
||||
/* Insert the new entry */
|
||||
entries[lo].key = sym;
|
||||
entries[lo].flags = flags;
|
||||
entries[lo].val = ptrval;
|
||||
entries = t->ptr;
|
||||
entries[t->size].key = sym;
|
||||
entries[t->size].flags = flags;
|
||||
entries[t->size].val = ptrval;
|
||||
t->size++;
|
||||
}
|
||||
|
||||
/* Retrieves a value from the method table (walks chain).
|
||||
/* Retrieves a value from the method table (walks chain, linear scan).
|
||||
Returns TRUE if found, FALSE if not found.
|
||||
On success, *pp and *fp are set. */
|
||||
static mrb_bool
|
||||
mt_get(mrb_state *mrb, mrb_mt_tbl *t, mrb_sym sym, union mrb_mt_ptr *pp, uint32_t *fp)
|
||||
{
|
||||
while (t) {
|
||||
if (t->size > 0) {
|
||||
mrb_mt_entry *entries = t->ptr;
|
||||
int lo = mt_bsearch_idx(entries, t->size, sym);
|
||||
if (lo < t->size && entries[lo].key == sym) {
|
||||
if (MRB_MT_REMOVED_P(entries[lo])) return FALSE; /* removed tombstone */
|
||||
*pp = entries[lo].val;
|
||||
*fp = entries[lo].flags;
|
||||
mrb_mt_entry *entries = t->ptr;
|
||||
for (int i = 0; i < t->size; i++) {
|
||||
if (entries[i].key == sym) {
|
||||
if (MRB_MT_REMOVED_P(entries[i])) return FALSE;
|
||||
*pp = entries[i].val;
|
||||
*fp = entries[i].flags;
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
@@ -140,27 +103,22 @@ mt_get(mrb_state *mrb, mrb_mt_tbl *t, mrb_sym sym, union mrb_mt_ptr *pp, uint32_
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Deletes an entry from the method table */
|
||||
/* Deletes an entry from the method table (swap with last) */
|
||||
static mrb_bool
|
||||
mt_del(mrb_state *mrb, mrb_mt_tbl *t, mrb_sym sym)
|
||||
{
|
||||
/* Return FALSE if table is null or empty */
|
||||
if (!t || t->size == 0) return FALSE;
|
||||
|
||||
mrb_mt_entry *entries = t->ptr;
|
||||
|
||||
/* Find the index of `sym` in a branch-free manner */
|
||||
int lo = mt_bsearch_idx(entries, t->size, sym);
|
||||
|
||||
/* If the key exists at index lo, remove it by shifting left */
|
||||
if (lo < t->size && entries[lo].key == sym) {
|
||||
memmove(&entries[lo], &entries[lo + 1],
|
||||
(t->size - lo - 1) * sizeof(mrb_mt_entry));
|
||||
t->size--;
|
||||
return TRUE;
|
||||
for (int i = 0; i < t->size; i++) {
|
||||
if (entries[i].key == sym) {
|
||||
t->size--;
|
||||
if (i < t->size) {
|
||||
entries[i] = entries[t->size];
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Key not found */
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -169,10 +127,9 @@ static mrb_bool
|
||||
mt_chain_has(mrb_mt_tbl *t, mrb_sym sym)
|
||||
{
|
||||
while (t) {
|
||||
if (t->size > 0) {
|
||||
mrb_mt_entry *entries = t->ptr;
|
||||
int lo = mt_bsearch_idx(entries, t->size, sym);
|
||||
if (lo < t->size && entries[lo].key == sym) return TRUE;
|
||||
mrb_mt_entry *entries = t->ptr;
|
||||
for (int i = 0; i < t->size; i++) {
|
||||
if (entries[i].key == sym) return TRUE;
|
||||
}
|
||||
t = t->next;
|
||||
}
|
||||
@@ -214,29 +171,11 @@ mt_free(mrb_state *mrb, mrb_mt_tbl *t)
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorts entries array by key symbol (insertion sort) */
|
||||
static void
|
||||
mt_sort(mrb_mt_entry *entries, int n)
|
||||
{
|
||||
for (int i = 1; i < n; i++) {
|
||||
mrb_mt_entry e = entries[i];
|
||||
mrb_sym sym = e.key;
|
||||
int j = i;
|
||||
while (j > 0 && entries[j-1].key > sym) {
|
||||
entries[j] = entries[j-1];
|
||||
j--;
|
||||
}
|
||||
entries[j] = e;
|
||||
}
|
||||
}
|
||||
|
||||
/* Sorts a static ROM table, sets readonly flag, and pushes it to the class */
|
||||
/* Pushes a ROM table layer onto the class's method table chain.
|
||||
The readonly flag is already set by MRB_MT_ROM_TAB(). */
|
||||
void
|
||||
mrb_mt_init_rom(struct RClass *c, mrb_mt_tbl *rom)
|
||||
{
|
||||
mt_sort(rom->ptr, rom->size);
|
||||
rom->alloc = rom->size | MRB_MT_READONLY_BIT;
|
||||
|
||||
/* push ROM layer */
|
||||
mrb_mt_tbl *t = c->mt;
|
||||
if (!t || mt_readonly_p(t)) {
|
||||
@@ -290,13 +229,14 @@ mrb_mt_foreach(mrb_state *mrb, struct RClass *c, mrb_mt_foreach_func *fn, void *
|
||||
if (layer != t) {
|
||||
mrb_bool shadowed = FALSE;
|
||||
for (mrb_mt_tbl *upper = t; upper != layer; upper = upper->next) {
|
||||
if (upper->size > 0) {
|
||||
int lo = mt_bsearch_idx(upper->ptr, upper->size, sym);
|
||||
if (lo < upper->size && upper->ptr[lo].key == sym) {
|
||||
mrb_mt_entry *up = upper->ptr;
|
||||
for (int j = 0; j < upper->size; j++) {
|
||||
if (up[j].key == sym) {
|
||||
shadowed = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (shadowed) break;
|
||||
}
|
||||
if (shadowed) continue;
|
||||
}
|
||||
@@ -4319,7 +4259,7 @@ static const struct RProc neq_proc = {
|
||||
};
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry bob_rom_entries[] = {
|
||||
static const mrb_mt_entry bob_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_obj_equal_m, MRB_OPSYM(eq), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_bob_not, MRB_OPSYM(not), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(mrb_obj_id_m, MRB_SYM(__id__), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
@@ -4334,7 +4274,7 @@ static mrb_mt_entry bob_rom_entries[] = {
|
||||
};
|
||||
static mrb_mt_tbl bob_rom_mt = MRB_MT_ROM_TAB(bob_rom_entries);
|
||||
|
||||
static mrb_mt_entry cls_rom_entries[] = {
|
||||
static const mrb_mt_entry cls_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_instance_alloc, MRB_SYM(allocate), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(mrb_do_nothing, MRB_SYM(inherited), MRB_MT_FUNC|MRB_MT_PRIVATE),
|
||||
MRB_MT_ENTRY(mrb_class_initialize, MRB_SYM(initialize), MRB_MT_FUNC|MRB_MT_PRIVATE),
|
||||
@@ -4342,7 +4282,7 @@ static mrb_mt_entry cls_rom_entries[] = {
|
||||
};
|
||||
static mrb_mt_tbl cls_rom_mt = MRB_MT_ROM_TAB(cls_rom_entries);
|
||||
|
||||
static mrb_mt_entry mod_rom_entries[] = {
|
||||
static const mrb_mt_entry mod_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_mod_eqq, MRB_OPSYM(eqq), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_mod_alias, MRB_SYM(alias_method), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_mod_ancestors, MRB_SYM(ancestors), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
|
||||
+1
-1
@@ -892,7 +892,7 @@ mrb_check_error(mrb_state *mrb)
|
||||
}
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry exception_rom_entries[] = {
|
||||
static const mrb_mt_entry exception_rom_entries[] = {
|
||||
MRB_MT_ENTRY(exc_exception, MRB_SYM(exception), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(exc_initialize, MRB_SYM(initialize), MRB_MT_FUNC|MRB_MT_PRIVATE),
|
||||
MRB_MT_ENTRY(exc_to_s, MRB_SYM(to_s), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
|
||||
+1
-1
@@ -2303,7 +2303,7 @@ mrb_hash_eql(mrb_state *mrb, mrb_value hash)
|
||||
}
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry hash_rom_entries[] = {
|
||||
static const mrb_mt_entry hash_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_hash_equal, MRB_OPSYM(eq), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_hash_aget, MRB_OPSYM(aref), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_hash_aset, MRB_OPSYM(aset), MRB_MT_FUNC),
|
||||
|
||||
+1
-1
@@ -666,7 +666,7 @@ mrb_p_m(mrb_state *mrb, mrb_value self)
|
||||
#endif
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry kernel_rom_entries[] = {
|
||||
static const mrb_mt_entry kernel_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_eqq_m, MRB_OPSYM(eqq), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_cmp_m, MRB_OPSYM(cmp), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_f_block_given_p_m, MRB_SYM_Q(block_given), MRB_MT_FUNC|MRB_MT_NOARG|MRB_MT_PRIVATE),
|
||||
|
||||
+3
-3
@@ -2299,14 +2299,14 @@ flo_hash(mrb_state *mrb, mrb_value flo)
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------*/
|
||||
static mrb_mt_entry numeric_rom_entries[] = {
|
||||
static const mrb_mt_entry numeric_rom_entries[] = {
|
||||
MRB_MT_ENTRY(num_finite_p, MRB_SYM_Q(finite), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(num_infinite_p, MRB_SYM_Q(infinite), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(num_eql, MRB_SYM_Q(eql), MRB_MT_FUNC),
|
||||
};
|
||||
static mrb_mt_tbl numeric_rom_mt = MRB_MT_ROM_TAB(numeric_rom_entries);
|
||||
|
||||
static mrb_mt_entry integer_rom_entries[] = {
|
||||
static const mrb_mt_entry integer_rom_entries[] = {
|
||||
MRB_MT_ENTRY(int_pow, MRB_OPSYM(pow), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(num_cmp, MRB_OPSYM(cmp), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(num_lt, MRB_OPSYM(lt), MRB_MT_FUNC),
|
||||
@@ -2342,7 +2342,7 @@ static mrb_mt_entry integer_rom_entries[] = {
|
||||
static mrb_mt_tbl integer_rom_mt = MRB_MT_ROM_TAB(integer_rom_entries);
|
||||
|
||||
#ifndef MRB_NO_FLOAT
|
||||
static mrb_mt_entry float_rom_entries[] = {
|
||||
static const mrb_mt_entry float_rom_entries[] = {
|
||||
MRB_MT_ENTRY(flo_pow, MRB_OPSYM(pow), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(flo_div, MRB_OPSYM(div), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(flo_div, MRB_SYM(quo), MRB_MT_FUNC),
|
||||
|
||||
+3
-3
@@ -322,7 +322,7 @@ false_to_s(mrb_state *mrb, mrb_value obj)
|
||||
}
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry nil_rom_entries[] = {
|
||||
static const mrb_mt_entry nil_rom_entries[] = {
|
||||
MRB_MT_ENTRY(false_and, MRB_OPSYM(and), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(false_or, MRB_OPSYM(or), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(false_xor, MRB_OPSYM(xor), MRB_MT_FUNC),
|
||||
@@ -332,7 +332,7 @@ static mrb_mt_entry nil_rom_entries[] = {
|
||||
};
|
||||
static mrb_mt_tbl nil_rom_mt = MRB_MT_ROM_TAB(nil_rom_entries);
|
||||
|
||||
static mrb_mt_entry true_rom_entries[] = {
|
||||
static const mrb_mt_entry true_rom_entries[] = {
|
||||
MRB_MT_ENTRY(true_and, MRB_OPSYM(and), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(true_or, MRB_OPSYM(or), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(true_xor, MRB_OPSYM(xor), MRB_MT_FUNC),
|
||||
@@ -341,7 +341,7 @@ static mrb_mt_entry true_rom_entries[] = {
|
||||
};
|
||||
static mrb_mt_tbl true_rom_mt = MRB_MT_ROM_TAB(true_rom_entries);
|
||||
|
||||
static mrb_mt_entry false_rom_entries[] = {
|
||||
static const mrb_mt_entry false_rom_entries[] = {
|
||||
MRB_MT_ENTRY(false_and, MRB_OPSYM(and), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(false_or, MRB_OPSYM(or), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(false_xor, MRB_OPSYM(xor), MRB_MT_FUNC),
|
||||
|
||||
+1
-1
@@ -545,7 +545,7 @@ mrb_proc_merge_lvar(mrb_state *mrb, mrb_irep *irep, struct REnv *env, int num, c
|
||||
}
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry proc_rom_entries[] = {
|
||||
static const mrb_mt_entry proc_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_proc_init_copy, MRB_SYM(initialize_copy), MRB_MT_FUNC|MRB_MT_PRIVATE),
|
||||
MRB_MT_ENTRY(proc_arity, MRB_SYM(arity), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(proc_eql, MRB_OPSYM(eq), MRB_MT_FUNC),
|
||||
|
||||
+1
-1
@@ -566,7 +566,7 @@ mrb_range_beg_len(mrb_state *mrb, mrb_value range, mrb_int *begp, mrb_int *lenp,
|
||||
}
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry range_rom_entries[] = {
|
||||
static const mrb_mt_entry range_rom_entries[] = {
|
||||
MRB_MT_ENTRY(range_beg, MRB_SYM(begin), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(range_end, MRB_SYM(end), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(range_eq, MRB_OPSYM(eq), MRB_MT_FUNC),
|
||||
|
||||
+1
-1
@@ -3508,7 +3508,7 @@ mrb_encoding(mrb_state *mrb, mrb_value self)
|
||||
}
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry string_rom_entries[] = {
|
||||
static const mrb_mt_entry string_rom_entries[] = {
|
||||
MRB_MT_ENTRY(mrb_str_bytesize, MRB_SYM(bytesize), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(mrb_str_cmp_m, MRB_OPSYM(cmp), MRB_MT_FUNC),
|
||||
MRB_MT_ENTRY(mrb_str_equal_m, MRB_OPSYM(eq), MRB_MT_FUNC),
|
||||
|
||||
+1
-1
@@ -1001,7 +1001,7 @@ sym_cmp(mrb_state *mrb, mrb_value s1)
|
||||
#undef lesser
|
||||
|
||||
/* ---------------------------*/
|
||||
static mrb_mt_entry symbol_rom_entries[] = {
|
||||
static const mrb_mt_entry symbol_rom_entries[] = {
|
||||
MRB_MT_ENTRY(sym_to_s, MRB_SYM(to_s), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(sym_name, MRB_SYM(name), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
MRB_MT_ENTRY(mrb_obj_itself, MRB_SYM(to_sym), MRB_MT_FUNC|MRB_MT_NOARG),
|
||||
|
||||
Reference in New Issue
Block a user