Feature #13174 ยป improve_id_table.patch
| id_table.c | ||
|---|---|---|
| #define ID_TABLE_DEBUG 0 | ||
| #endif | ||
| #if ID_TABLE_DEBUG == 0 | ||
| #define NDEBUG | ||
| #endif | ||
| #include "ruby_assert.h" | ||
| typedef rb_id_serial_t id_key_t; | ||
| static inline ID | ||
| ... | ... | |
|    uses mark-bit on collisions - need extra 1 bit, | ||
|    ID is strictly 3 bits larger than rb_id_serial_t */ | ||
| typedef struct rb_id_item { | ||
|     id_key_t key; | ||
| #if SIZEOF_VALUE == 8 | ||
|     int      collision; | ||
| #endif | ||
|     VALUE    val; | ||
| } item_t; | ||
| struct rb_id_table { | ||
|     int capa; | ||
|     int num; | ||
|     int used; | ||
|     item_t *items; | ||
|     id_key_t *keys; | ||
| }; | ||
| #if SIZEOF_VALUE == 8 | ||
| #define ITEM_GET_KEY(tbl, i) ((tbl)->items[i].key) | ||
| #define ITEM_KEY_ISSET(tbl, i) ((tbl)->items[i].key) | ||
| #define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].collision) | ||
| #define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].collision = 1) | ||
| #define ITEM_GET_KEY(tbl, i) ((tbl)->keys[i] >> 1) | ||
| #define ITEM_KEY_ISSET(tbl, i) ((tbl)->keys[i] > 1) | ||
| #define ITEM_COLLIDED(tbl, i) ((tbl)->keys[i] & 1) | ||
| #define ITEM_SET_COLLIDED(tbl, i) ((tbl)->keys[i] |= 1) | ||
| static inline void | ||
| ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key) | ||
| { | ||
|     tbl->items[i].key = key; | ||
|     tbl->keys[i] = (key << 1) | ITEM_COLLIDED(tbl, i); | ||
| } | ||
| #else | ||
| #define ITEM_GET_KEY(tbl, i) ((tbl)->items[i].key >> 1) | ||
| #define ITEM_KEY_ISSET(tbl, i) ((tbl)->items[i].key > 1) | ||
| #define ITEM_COLLIDED(tbl, i) ((tbl)->items[i].key & 1) | ||
| #define ITEM_SET_COLLIDED(tbl, i) ((tbl)->items[i].key |= 1) | ||
| #define ID_TABLE_VALUES(tbl) ((VALUE*)((tbl)->keys+(tbl)->capa)) | ||
| #define ITEM_GET_VALUE(tbl, i) (ID_TABLE_VALUES(tbl)[i]) | ||
| static inline void | ||
| ITEM_SET_KEY(struct rb_id_table *tbl, int i, id_key_t key) | ||
| ITEM_SET_VALUE(struct rb_id_table *tbl, int i, VALUE val) | ||
| { | ||
|     tbl->items[i].key = (key << 1) | ITEM_COLLIDED(tbl, i); | ||
|     ID_TABLE_VALUES(tbl)[i] = val; | ||
| } | ||
| #endif | ||
| #define ITEM_SIZE (sizeof(id_key_t) + sizeof(VALUE)) | ||
| static inline int | ||
| round_capa(int capa) | ||
| ... | ... | |
|     if (capa > 0) { | ||
| 	capa = round_capa(capa); | ||
| 	tbl->capa = (int)capa; | ||
| 	tbl->items = ZALLOC_N(item_t, capa); | ||
| 	tbl->keys = (id_key_t*)ruby_xcalloc(capa, ITEM_SIZE); | ||
|     } | ||
|     return tbl; | ||
| } | ||
| ... | ... | |
| void | ||
| rb_id_table_free(struct rb_id_table *tbl) | ||
| { | ||
|     xfree(tbl->items); | ||
|     xfree(tbl->keys); | ||
|     xfree(tbl); | ||
| } | ||
| ... | ... | |
| { | ||
|     tbl->num = 0; | ||
|     tbl->used = 0; | ||
|     MEMZERO(tbl->items, item_t, tbl->capa); | ||
|     memset(tbl->keys, 0, tbl->capa * ITEM_SIZE); | ||
| } | ||
| size_t | ||
| ... | ... | |
| size_t | ||
| rb_id_table_memsize(const struct rb_id_table *tbl) | ||
| { | ||
|     return sizeof(item_t) * tbl->capa + sizeof(struct rb_id_table); | ||
|     return ITEM_SIZE * (size_t)tbl->capa + sizeof(struct rb_id_table); | ||
| } | ||
| static int | ||
| ... | ... | |
|     int mask = tbl->capa - 1; | ||
|     int ix = key & mask; | ||
|     int d = 1; | ||
|     assert(key != 0); | ||
| #if ID_TABLE_DEBUG | ||
|     assert(key > 0); | ||
| #endif | ||
|     while (ITEM_KEY_ISSET(tbl, ix)) { | ||
| 	ITEM_SET_COLLIDED(tbl, ix); | ||
| 	ix = (ix + d) & mask; | ||
| ... | ... | |
| 	tbl->used++; | ||
|     } | ||
|     ITEM_SET_KEY(tbl, ix, key); | ||
|     tbl->items[ix].val = val; | ||
|     ITEM_SET_VALUE(tbl, ix, val); | ||
| } | ||
| static int | ||
| ... | ... | |
| 	} | ||
| 	tbl->num--; | ||
| 	ITEM_SET_KEY(tbl, ix, 0); | ||
| 	tbl->items[ix].val = 0; | ||
| 	ITEM_SET_VALUE(tbl, ix, 0); | ||
| 	return TRUE; | ||
|     } else { | ||
| 	return FALSE; | ||
| ... | ... | |
| static void | ||
| hash_table_extend(struct rb_id_table* tbl) | ||
| { | ||
|     /* fill rate 66% */ | ||
|     if (tbl->used + (tbl->used >> 1) >= tbl->capa) { | ||
| 	int new_cap = round_capa(tbl->num + (tbl->num >> 1)); | ||
| 	struct rb_id_table tmp_tbl; | ||
| 	int i; | ||
| 	item_t* old; | ||
| 	struct rb_id_table tmp_tbl = {0, 0, 0}; | ||
| 	if (new_cap < tbl->capa) { | ||
| 	    new_cap = round_capa(tbl->used + (tbl->used >> 1)); | ||
| 	} | ||
| 	tmp_tbl.capa = new_cap; | ||
| 	tmp_tbl.items = ZALLOC_N(item_t, new_cap); | ||
| 	id_key_t* old; | ||
| 	VALUE* values = ID_TABLE_VALUES(tbl); | ||
| 	rb_id_table_init(&tmp_tbl, tbl->num + (tbl->num >> 1) + 1); | ||
| 	for (i = 0; i < tbl->capa; i++) { | ||
| 	    id_key_t key = ITEM_GET_KEY(tbl, i); | ||
| 	    if (key != 0) { | ||
| 		hash_table_raw_insert(&tmp_tbl, key, tbl->items[i].val); | ||
| 		hash_table_raw_insert(&tmp_tbl, key, values[i]); | ||
| 	    } | ||
| 	} | ||
| 	old = tbl->items; | ||
| 	old = tbl->keys; | ||
| 	*tbl = tmp_tbl; | ||
| 	xfree(old); | ||
|     } | ||
| ... | ... | |
|     int index = hash_table_index(tbl, key); | ||
|     if (index >= 0) { | ||
| 	*valp = tbl->items[index].val; | ||
| 	*valp = ITEM_GET_VALUE(tbl, index); | ||
| 	return TRUE; | ||
|     } | ||
|     else { | ||
| ... | ... | |
|     const int index = hash_table_index(tbl, key); | ||
|     if (index >= 0) { | ||
| 	tbl->items[index].val = val; | ||
| 	ITEM_SET_VALUE(tbl, index, val); | ||
|     } | ||
|     else { | ||
| 	hash_table_extend(tbl); | ||
| ... | ... | |
|     for (i=0; i<capa; i++) { | ||
| 	if (ITEM_KEY_ISSET(tbl, i)) { | ||
| 	    const id_key_t key = ITEM_GET_KEY(tbl, i); | ||
| 	    enum rb_id_table_iterator_result ret = (*func)(key2id(key), tbl->items[i].val, data); | ||
| 	    assert(key != 0); | ||
| 	    const VALUE val = ITEM_GET_VALUE(tbl, i); | ||
| 	    enum rb_id_table_iterator_result ret = (*func)(key2id(key), val, data); | ||
| 	    if (ret == ID_TABLE_DELETE) | ||
| 		hash_delete_index(tbl, i); | ||
| ... | ... | |
|     for (i=0; i<capa; i++) { | ||
| 	if (ITEM_KEY_ISSET(tbl, i)) { | ||
| 	    enum rb_id_table_iterator_result ret = (*func)(tbl->items[i].val, data); | ||
| 	    const VALUE val = ITEM_GET_VALUE(tbl, i); | ||
| 	    enum rb_id_table_iterator_result ret = (*func)(val, data); | ||
| 	    if (ret == ID_TABLE_DELETE) | ||
| 		hash_delete_index(tbl, i); | ||
| symbol.c | ||
|---|---|---|
| #include "symbol.h" | ||
| #include "gc.h" | ||
| #include "probes.h" | ||
| #include "ruby_assert.h" | ||
| #ifndef SYMBOL_DEBUG | ||
| # define SYMBOL_DEBUG 0 | ||
| ... | ... | |
| { | ||
|     rb_id_serial_t next_serial = global_symbols.last_id + 1; | ||
|     if (next_serial == 0) { | ||
|     if (sizeof(next_serial) >= sizeof(ID) && | ||
| 	    next_serial >= (rb_id_serial_t)(~(ID)0 >> (ID_SCOPE_SHIFT + RUBY_SPECIAL_SHIFT))) { | ||
| 	return (ID)-1; | ||
|     } | ||
|     /* need at lest 1 bit for collision mark in id_table */ | ||
|     else if (sizeof(next_serial) < sizeof(ID) && | ||
| 	    next_serial >= ~(rb_id_serial_t)0 >> 1) { | ||
| 	return (ID)-1; | ||
|     } | ||
|     else { | ||
| ... | ... | |
| 	    VALUE fstr = RSYMBOL(sym)->fstr; | ||
| 	    ID num = next_id_base(); | ||
| 	    if (num == (ID)-1) { | ||
| 		fstr = rb_str_ellipsize(fstr, 20); | ||
| 		rb_raise(rb_eRuntimeError, | ||
| 				"symbol table overflow (symbol %"PRIsVALUE")", | ||
| 				fstr); | ||
| 	    } | ||
| 	    RSYMBOL(sym)->id = id |= num; | ||
| 	    /* make it permanent object */ | ||
| 	    set_id_entry(rb_id_to_serial(num), fstr, sym); | ||
| ... | ... | |
| ID | ||
| rb_make_internal_id(void) | ||
| { | ||
|     return next_id_base() | ID_INTERNAL | ID_STATIC_SYM; | ||
|     ID nid = next_id_base(); | ||
|     /* make_internal_id called very rarely during initialization, | ||
|      * so don't bother with exception */ | ||
|     assert(nid != (ID)-1); | ||
|     return nid | ID_INTERNAL | ID_STATIC_SYM; | ||
| } | ||
| static int | ||
| -  | ||
| id_table.c | ||
|---|---|---|
|     if (tbl->capa > 0) { | ||
| 	int mask = tbl->capa - 1; | ||
| 	int ix = key & mask; | ||
| 	id_key_t mix = tbl->capa > 64 ? key : 0; | ||
| 	int d = 1; | ||
| 	while (key != ITEM_GET_KEY(tbl, ix)) { | ||
| 	    if (!ITEM_COLLIDED(tbl, ix)) | ||
| 		return -1; | ||
| 	    ix = (ix + d) & mask; | ||
| 	    d++; | ||
| 	    d += 1 + (mix >>= 7); | ||
| 	} | ||
| 	return ix; | ||
|     } | ||
| ... | ... | |
| { | ||
|     int mask = tbl->capa - 1; | ||
|     int ix = key & mask; | ||
|     id_key_t mix = tbl->capa > 64 ? key : 0; | ||
|     int d = 1; | ||
| #if ID_TABLE_DEBUG | ||
|     assert(key > 0); | ||
| ... | ... | |
|     while (ITEM_KEY_ISSET(tbl, ix)) { | ||
| 	ITEM_SET_COLLIDED(tbl, ix); | ||
| 	ix = (ix + d) & mask; | ||
| 	d++; | ||
| 	d += 1 + (mix >>= 7); | ||
|     } | ||
|     tbl->num++; | ||
|     if (!ITEM_COLLIDED(tbl, ix)) { | ||