Skip to content

Commit 1e03710

Browse files
etiennebarriebyroot
andcommitted
Precompute hash only once when interning string literals
When a fake string is interned, use the capa field to store the string hash. This lets us compute it once for hash lookup and embedding the hash in the interned string. Co-authored-by: Jean Boussier <[email protected]>
1 parent 09874e9 commit 1e03710

File tree

1 file changed

+26
-6
lines changed

1 file changed

+26
-6
lines changed

string.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -352,9 +352,24 @@ static int fstring_cmp(VALUE a, VALUE b);
352352

353353
static VALUE register_fstring(VALUE str, bool copy, bool precompute_hash);
354354

355+
#if SIZEOF_LONG == SIZEOF_VOIDP
356+
static st_index_t
357+
fstring_hash(VALUE str)
358+
{
359+
if (FL_TEST_RAW(str, STR_FAKESTR)) {
360+
// register_fstring precomputes the hash and stores it in capa for fake strings
361+
return (st_index_t)RSTRING(str)->as.heap.aux.capa;
362+
}
363+
else {
364+
return rb_str_hash(str);
365+
}
366+
}
367+
#else
368+
#define fstring_hash rb_str_hash
369+
#endif
355370
const struct st_hash_type rb_fstring_hash_type = {
356371
fstring_cmp,
357-
rb_str_hash,
372+
fstring_hash,
358373
};
359374

360375
#define BARE_STRING_P(str) (!FL_ANY_RAW(str, FL_EXIVAR) && RBASIC_CLASS(str) == rb_cString)
@@ -371,7 +386,7 @@ str_do_hash(VALUE str)
371386
}
372387

373388
static VALUE
374-
str_precompute_hash(VALUE str)
389+
str_store_precomputed_hash(VALUE str, st_index_t hash)
375390
{
376391
RUBY_ASSERT(!FL_TEST_RAW(str, STR_PRECOMPUTED_HASH));
377392
RUBY_ASSERT(STR_EMBED_P(str));
@@ -382,7 +397,6 @@ str_precompute_hash(VALUE str)
382397
RUBY_ASSERT(free_bytes >= sizeof(st_index_t));
383398
#endif
384399

385-
st_index_t hash = str_do_hash(str);
386400
memcpy(RSTRING_END(str) + TERM_LEN(str), &hash, sizeof(hash));
387401

388402
FL_SET(str, STR_PRECOMPUTED_HASH);
@@ -399,7 +413,6 @@ struct fstr_update_arg {
399413
static int
400414
fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t data, int existing)
401415
{
402-
403416
struct fstr_update_arg *arg = (struct fstr_update_arg *)data;
404417
VALUE str = (VALUE)*key;
405418

@@ -429,7 +442,7 @@ fstr_update_callback(st_data_t *key, st_data_t *value, st_data_t data, int exist
429442
STR_SET_LEN(new_str, RSTRING_LEN(str));
430443
TERM_FILL(RSTRING_END(new_str), TERM_LEN(str));
431444
rb_enc_copy(new_str, str);
432-
str_precompute_hash(new_str);
445+
str_store_precomputed_hash(new_str, fstring_hash(str));
433446
}
434447
else {
435448
new_str = str_new(rb_cString, RSTRING(str)->as.heap.ptr, RSTRING(str)->len);
@@ -509,6 +522,14 @@ register_fstring(VALUE str, bool copy, bool precompute_hash)
509522
.precompute_hash = precompute_hash
510523
};
511524

525+
#if SIZEOF_VOIDP == SIZEOF_LONG
526+
if (FL_TEST_RAW(str, STR_FAKESTR)) {
527+
// if the string hasn't been interned, we'll need the hash twice, so we
528+
// compute it once and store it in capa
529+
RSTRING(str)->as.heap.aux.capa = (long)str_do_hash(str);
530+
}
531+
#endif
532+
512533
RB_VM_LOCK_ENTER();
513534
{
514535
st_table *frozen_strings = rb_vm_fstring_table();
@@ -531,7 +552,6 @@ static VALUE
531552
setup_fake_str(struct RString *fake_str, const char *name, long len, int encidx)
532553
{
533554
fake_str->basic.flags = T_STRING|RSTRING_NOEMBED|STR_NOFREE|STR_FAKESTR;
534-
/* SHARED to be allocated by the callback */
535555

536556
if (!name) {
537557
RUBY_ASSERT_ALWAYS(len == 0);

0 commit comments

Comments
 (0)