From b5c6c0122f5b010cb5f43e7a236c4ba2b1d56a2a Mon Sep 17 00:00:00 2001 From: Alexander Momchilov Date: Mon, 18 Dec 2023 03:17:44 -0500 Subject: [PATCH] compare_by_identity: remove alloc for empty Hash For non-empty Hashes, this function needs to rehash all the stored values (using the new `compare` and `hash` functions from `identhash`). It does so by writing into a newly allocated `tmp` Hash, and then transferring ownership of its st table into `self`. For empty Hashes, we can skip allocating this `tmp`, because there's nothing to re-hash. We can just modify our new st table's `type` in-place. --- hash.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/hash.c b/hash.c index cf83675c70..f6525ba4a5 100644 --- a/hash.c +++ b/hash.c @@ -4380,15 +4380,22 @@ rb_hash_compare_by_id(VALUE hash) ar_force_convert_table(hash, __FILE__, __LINE__); HASH_ASSERT(RHASH_ST_TABLE_P(hash)); - tmp = hash_alloc(0); - hash_st_table_init(tmp, &identhash, RHASH_SIZE(hash)); - identtable = RHASH_ST_TABLE(tmp); + if (RHASH_TABLE_EMPTY_P(hash)) { + // Fast path: There's nothing to rehash, so we don't need a `tmp` table. + RHASH_ST_TABLE(hash)->type = &identhash; + } else { + // Slow path: Need to rehash the members of `self` into a new + // `tmp` table using the new `identhash` compare/hash functions. + tmp = hash_alloc(0); + hash_st_table_init(tmp, &identhash, RHASH_SIZE(hash)); + identtable = RHASH_ST_TABLE(tmp); - rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp); + rb_hash_foreach(hash, rb_hash_rehash_i, (VALUE)tmp); - rb_hash_free(hash); - RHASH_ST_TABLE_SET(hash, identtable); - RHASH_ST_CLEAR(tmp); + rb_hash_free(hash); + RHASH_ST_TABLE_SET(hash, identtable); + RHASH_ST_CLEAR(tmp); + } return hash; }