From 8e96d6f792ccc840b804a4fd443d0b0064b54752 Mon Sep 17 00:00:00 2001 From: Jean Boussier Date: Sat, 11 Apr 2026 15:42:19 +0200 Subject: rb_gc_obj_optimal_size: don't enlarge small AR table hashes Ref: https://github.com/ruby/ruby/pull/16653 Frozen hashes backed by AR tables can be smaller than 160B. --- gc.c | 7 ++++++- hash.c | 16 ---------------- internal/hash.h | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 17 deletions(-) diff --git a/gc.c b/gc.c index 3cf3bdebce..8a0d051e8f 100644 --- a/gc.c +++ b/gc.c @@ -3601,7 +3601,12 @@ rb_gc_obj_optimal_size(VALUE obj) } case T_HASH: - return sizeof(struct RHash) + (RHASH_ST_TABLE_P(obj) ? sizeof(st_table) : sizeof(ar_table)); + { + if (RB_OBJ_FROZEN(obj) && RHASH_AR_TABLE_P(obj)) { + return sizeof(struct RHash) + offsetof(ar_table, pairs) + RHASH_AR_TABLE_BOUND(obj) * sizeof(ar_table_pair); + } + return sizeof(struct RHash) + (RHASH_ST_TABLE_P(obj) ? sizeof(st_table) : sizeof(ar_table)); + } default: return 0; diff --git a/hash.c b/hash.c index 751d4564d5..4321f479fd 100644 --- a/hash.c +++ b/hash.c @@ -471,24 +471,8 @@ ar_set_entry(VALUE hash, unsigned int index, st_data_t key, st_data_t val, st_ha #define RHASH_AR_TABLE_SIZE(h) (HASH_ASSERT(RHASH_AR_TABLE_P(h)), \ RHASH_AR_TABLE_SIZE_RAW(h)) -#define RHASH_AR_TABLE_BOUND_RAW(h) \ - ((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \ - (RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT))) - -#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s) -#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type) - #define HASH_ASSERT(expr) RUBY_ASSERT_MESG_WHEN(HASH_DEBUG, expr, #expr) -static inline unsigned int -RHASH_AR_TABLE_BOUND(VALUE h) -{ - HASH_ASSERT(RHASH_AR_TABLE_P(h)); - const unsigned int bound = RHASH_AR_TABLE_BOUND_RAW(h); - HASH_ASSERT(bound <= RHASH_AR_TABLE_MAX_SIZE); - return bound; -} - #if HASH_DEBUG #define hash_verify(hash) hash_verify_(hash, __FILE__, __LINE__) diff --git a/internal/hash.h b/internal/hash.h index 3a5be14ad7..8c5511e82b 100644 --- a/internal/hash.h +++ b/internal/hash.h @@ -193,4 +193,20 @@ RHASH_AR_TABLE_SIZE_RAW(VALUE h) return (unsigned)ret; } +#define RHASH_AR_TABLE_BOUND_RAW(h) \ + ((unsigned int)((RBASIC(h)->flags >> RHASH_AR_TABLE_BOUND_SHIFT) & \ + (RHASH_AR_TABLE_BOUND_MASK >> RHASH_AR_TABLE_BOUND_SHIFT))) + +#define RHASH_ST_TABLE_SET(h, s) rb_hash_st_table_set(h, s) +#define RHASH_TYPE(hash) (RHASH_AR_TABLE_P(hash) ? &objhash : RHASH_ST_TABLE(hash)->type) + +static inline unsigned int +RHASH_AR_TABLE_BOUND(VALUE h) +{ + RUBY_ASSERT(RHASH_AR_TABLE_P(h)); + const unsigned int bound = RHASH_AR_TABLE_BOUND_RAW(h); + RUBY_ASSERT(bound <= RHASH_AR_TABLE_MAX_SIZE); + return bound; +} + #endif /* INTERNAL_HASH_H */ -- cgit v1.2.3