From f70c081b8ffe9d9e0f189cf5fd38ed6e0426d21b Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Fri, 10 Apr 2026 09:34:36 +0200 Subject: 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. --- compile.c | 33 +++++++++++++++++++++------------ hash.c | 8 ++++++++ internal/hash.h | 1 + 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; itype = &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 @@ -1490,6 +1490,14 @@ rb_hash_new_with_size(st_index_t size) return ret; } +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) { 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); -- cgit v1.2.3