summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean Boussier <jean.boussier@gmail.com>2026-04-10 09:34:36 +0200
committerJean Boussier <jean.boussier@gmail.com>2026-04-11 14:02:54 +0200
commitf70c081b8ffe9d9e0f189cf5fd38ed6e0426d21b (patch)
treece8eacc69512d88ebe99eb4130b6577151ac2092
parentc0d86a0103de7130943d54b4a290b76ec7e0c135 (diff)
Always allocate CDHASH in 80B slots
Up to size 8, `rb_hash_new_with_size()` will allocate a 160B slot to fit a full `ar_table`. But in the case of CDHASH, we immediately assign a custom hash type, which trigger the conversion into an `st_table`, potentially wasting 80B.
-rw-r--r--compile.c33
-rw-r--r--hash.c8
-rw-r--r--internal/hash.h1
3 files changed, 30 insertions, 12 deletions
diff --git a/compile.c b/compile.c
index ac1bdfe0f9..68cf715c4e 100644
--- a/compile.c
+++ b/compile.c
@@ -7067,7 +7067,7 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
DECL_ANCHOR(body_seq);
DECL_ANCHOR(cond_seq);
int only_special_literals = 1;
- VALUE literals = rb_hash_new();
+ VALUE literals = rb_hash_new_with_size_and_type(0, 0, &cdhash_type);
int line;
enum node_type type;
const NODE *line_node;
@@ -7078,8 +7078,6 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
INIT_ANCHOR(body_seq);
INIT_ANCHOR(cond_seq);
- RHASH_TBL_RAW(literals)->type = &cdhash_type;
-
CHECK(COMPILE(head, "case base", RNODE_CASE(node)->nd_head));
branches = decl_branch_base(iseq, PTR2NUM(node), nd_code_loc(node), "case");
@@ -7165,7 +7163,6 @@ compile_case(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *const orig_nod
if (only_special_literals && ISEQ_COMPILE_DATA(iseq)->option->specialized_instruction) {
ADD_INSN(ret, orig_node, dup);
- rb_obj_hide(literals);
ADD_INSN2(ret, orig_node, opt_case_dispatch, literals, elselabel);
RB_OBJ_WRITTEN(iseq, Qundef, literals);
LABEL_REF(elselabel);
@@ -12167,9 +12164,8 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
case TS_CDHASH:
{
int i;
- VALUE map = rb_hash_new_with_size(RARRAY_LEN(op)/2);
+ VALUE map = rb_hash_new_with_size_and_type(0, RARRAY_LEN(op)/2, &cdhash_type);
- RHASH_TBL_RAW(map)->type = &cdhash_type;
op = rb_to_array_type(op);
for (i=0; i<RARRAY_LEN(op); i+=2) {
VALUE key = RARRAY_AREF(op, i);
@@ -12179,7 +12175,7 @@ iseq_build_from_ary_body(rb_iseq_t *iseq, LINK_ANCHOR *const anchor,
rb_hash_aset(map, key, (VALUE)label | 1);
}
RB_GC_GUARD(op);
- RB_OBJ_SET_SHAREABLE(rb_obj_hide(map)); // allow mutation while compiling
+ RB_OBJ_SET_SHAREABLE(map); // allow mutation while compiling
argv[j] = map;
RB_OBJ_WRITTEN(iseq, Qundef, map);
}
@@ -13004,6 +13000,20 @@ ibf_dump_code(struct ibf_dump *dump, const rb_iseq_t *iseq)
return offset;
}
+static int
+cdhash_copy_i(st_data_t key, st_data_t val, st_data_t arg)
+{
+ rb_hash_aset((VALUE)arg, (VALUE)key, (VALUE)val);
+ return ST_CONTINUE;
+}
+
+static VALUE
+cdhash_copy(VALUE dest, VALUE src)
+{
+ rb_hash_stlike_foreach(src, cdhash_copy_i, dest);
+ return dest;
+}
+
static VALUE *
ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecode_offset, ibf_offset_t bytecode_size, unsigned int iseq_size)
{
@@ -13058,11 +13068,10 @@ ibf_load_code(const struct ibf_load *load, rb_iseq_t *iseq, ibf_offset_t bytecod
case TS_CDHASH:
{
VALUE op = ibf_load_small_value(load, &reading_pos);
- VALUE v = ibf_load_object(load, op);
- v = rb_hash_dup(v); // hash dumped as frozen
- RHASH_TBL_RAW(v)->type = &cdhash_type;
- rb_hash_rehash(v); // hash function changed
- RB_OBJ_SET_SHAREABLE(freeze_hide_obj(v));
+ VALUE src = ibf_load_object(load, op);
+ VALUE v = rb_hash_new_with_size_and_type(0, RHASH_SIZE(src), &cdhash_type);
+ rb_hash_rehash(cdhash_copy(v, src));
+ RB_OBJ_SET_SHAREABLE(v);
// Overwrite the existing hash in the object list. This
// is to keep the object alive during load time.
diff --git a/hash.c b/hash.c
index 8507c9c8f5..751d4564d5 100644
--- a/hash.c
+++ b/hash.c
@@ -1491,6 +1491,14 @@ rb_hash_new_with_size(st_index_t size)
}
VALUE
+rb_hash_new_with_size_and_type(VALUE klass, st_index_t size, const struct st_hash_type *type)
+{
+ VALUE ret = hash_alloc_flags(klass, 0, Qnil, true);
+ hash_st_table_init(ret, type, size);
+ return ret;
+}
+
+VALUE
rb_hash_new_capa(long capa)
{
return rb_hash_new_with_size((st_index_t)capa);
diff --git a/internal/hash.h b/internal/hash.h
index 9688478d1e..3a5be14ad7 100644
--- a/internal/hash.h
+++ b/internal/hash.h
@@ -112,6 +112,7 @@ int rb_hash_stlike_foreach(VALUE hash, st_foreach_callback_func *func, st_data_t
RUBY_SYMBOL_EXPORT_END
VALUE rb_hash_new_with_size(st_index_t size);
+VALUE rb_hash_new_with_size_and_type(VALUE klass, st_index_t size, const struct st_hash_type *type);
VALUE rb_hash_resurrect(VALUE hash);
int rb_hash_stlike_lookup(VALUE hash, st_data_t key, st_data_t *pval);
VALUE rb_hash_keys(VALUE hash);