mirror of
https://github.com/ruby/ruby.git
synced 2026-10-29 00:46:26 +08:00
Dynamic ar_table size
Instead of always allocating `ar_table` backed hashes in 160B
slots for them to have capacity of `RHASH_AR_TABLE_MAX_SIZE` (`8`),
they can now be allocated in any slot size between `64B` and `160B`.
`64B` is the minimum slot size that still allows transitioning to
an `st_table` if necessary.
The assumption is that there is a large number of literal hashes
and keyword argument hashes being built, and the overwhelming majority
of them aren't appended to.
When this assumption hold, this patch saves memory and reduce GC
pressure by allocating in a smaller slot than before.
However when the assumption doesn't hold, it has the oppositve effect
as the Hash will transition to an `st_table` when before it may have
stayed an `ar_table`.
Currently the main blocked for this change is that the compiler
has a tendency to generate code that make the final Hash size unknown.
For instance: `{a: 4, b: 4 + 1}` is compiled as:
```
0000 duphash {a: 4} ( 1)[Li]
0002 putspecialobject 1
0004 swap
0005 putobject :b
0007 putobject 4
0009 putobject_INT2FIX_1_
0010 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
0012 opt_send_without_block <calldata!mid:core#hash_merge_ptr, argc:3, ARGS_SIMPLE>
```
Here `duphash` has no information about how large of a hash it needs to allocate.
For this patch to be more efficient the compiler should generate something like:
```
0000 putobject {a: 4} ( 1)[Li]
0002 putspecialobject 1
0004 swap
0005 putobject :b
0007 putobject 4
0009 putobject_INT2FIX_1_
0010 opt_plus <calldata!mid:+, argc:1, ARGS_SIMPLE>[CcCr]
0012 opt_send_without_block <calldata!mid:core#hash_merge_ptr, argc:3, ARGS_SIMPLE>
```
So that it's `hash_merge_ptr` that is responsible for allocating the final hash,
given it does have a much better clue of how large it would need to be.
Demo:
```ruby
require 'objspace'
def size(hash)
puts "size: #{hash.size}\tmemsize: #{ObjectSpace.memsize_of(hash)}"
end
size({})
size(a:1)
size(a:1, b:2)
size(a:1, b:2, c:3)
size(a:1, b:2, c:3, d:4)
size(a:1, b:2, c:3, d:4, e:5)
size(a:1, b:2, c:3, d:4, e:5, f:6)
size(a:1, b:2, c:3, d:4, e:5, f:6, g:7)
size(a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8)
size(a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, i:9)
```
Before:
```
size: 0 memsize: 160
size: 1 memsize: 160
size: 2 memsize: 160
size: 3 memsize: 160
size: 4 memsize: 160
size: 5 memsize: 160
size: 6 memsize: 160
size: 7 memsize: 160
size: 8 memsize: 160
size: 9 memsize: 464
```
After:
```
size: 0 memsize: 64
size: 1 memsize: 64
size: 2 memsize: 64
size: 3 memsize: 80
size: 4 memsize: 96
size: 5 memsize: 128
size: 6 memsize: 128
size: 7 memsize: 160
size: 8 memsize: 160
size: 9 memsize: 448
```
This commit is contained in:
1 parent
6d4a6a939f
commit
60a4894bdb
10 files changed
+206
-44
Notes:
git
2026-08-25 13:25:32 +00:00
No files matched your search
@@ -3699,10 +3699,17 @@ rb_gc_obj_optimal_size(VALUE obj)
|
||||
|
||||
case T_HASH:
|
||||
{
|
||||
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);
|
||||
const size_t st_size = sizeof(struct RHash) + sizeof(st_table);
|
||||
if (RHASH_ST_TABLE_P(obj)) {
|
||||
return st_size;
|
||||
}
|
||||
return sizeof(struct RHash) + (RHASH_ST_TABLE_P(obj) ? sizeof(st_table) : sizeof(ar_table));
|
||||
|
||||
const size_t ar_size = sizeof(struct RHash) + offsetof(ar_table, pairs) + RHASH_AR_TABLE_BOUND(obj) * sizeof(ar_table_pair);
|
||||
if (OBJ_FROZEN(obj) || ar_size > st_size) {
|
||||
return ar_size;
|
||||
}
|
||||
|
||||
return st_size;
|
||||
}
|
||||
|
||||
default:
|
||||
|
||||
@@ -412,7 +412,26 @@ typedef st_index_t st_hash_t;
|
||||
static inline unsigned int
|
||||
RHASH_AR_TABLE_MAX_BOUND(VALUE h)
|
||||
{
|
||||
return RHASH_AR_TABLE_MAX_SIZE;
|
||||
size_t usable_space = rb_obj_shape_slot_size(h) - sizeof(struct RHash) - offsetof(ar_table, pairs);
|
||||
usable_space /= sizeof(ar_table_pair);
|
||||
#if SIZEOF_VALUE == 8
|
||||
RBIMPL_ASSERT_OR_ASSUME(usable_space <= RHASH_AR_TABLE_MAX_SIZE);
|
||||
return (unsigned)usable_space;
|
||||
#else
|
||||
return usable_space <= RHASH_AR_TABLE_MAX_SIZE ? (unsigned)usable_space : RHASH_AR_TABLE_MAX_SIZE;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
ar_table_memsize(size_t capa)
|
||||
{
|
||||
return offsetof(ar_table, pairs) + capa * sizeof(ar_table_pair);
|
||||
}
|
||||
|
||||
static inline size_t
|
||||
ar_memsize(size_t capa)
|
||||
{
|
||||
return sizeof(struct RHash) + ar_table_memsize(capa);
|
||||
}
|
||||
|
||||
#define RHASH_AR_TABLE_CONVERTED_TO_ST_TABLE (RHASH_AR_TABLE_MAX_SIZE + 1)
|
||||
@@ -539,6 +558,7 @@ RHASH_TABLE_EMPTY_P(VALUE hash)
|
||||
static void
|
||||
hash_st_table_init(VALUE hash, const struct st_hash_type *type, st_index_t size)
|
||||
{
|
||||
RUBY_ASSERT(rb_gc_obj_slot_size(hash) >= sizeof(struct RHash) + sizeof(st_table));
|
||||
st_init_existing_table_with_size(RHASH_ST_TABLE(hash), type, size);
|
||||
RHASH_SET_ST_FLAG(hash);
|
||||
}
|
||||
@@ -603,11 +623,10 @@ RHASH_AR_TABLE_SIZE_DEC(VALUE h)
|
||||
static inline void
|
||||
RHASH_AR_TABLE_CLEAR(VALUE h)
|
||||
{
|
||||
RUBY_ASSERT(rb_gc_obj_slot_size(h) >= sizeof(struct RHash) + sizeof(ar_table));
|
||||
RBASIC(h)->flags &= ~RHASH_AR_TABLE_SIZE_MASK;
|
||||
RBASIC(h)->flags &= ~RHASH_AR_TABLE_BOUND_MASK;
|
||||
|
||||
memset(RHASH_AR_TABLE(h), 0, sizeof(ar_table));
|
||||
memset(RHASH_AR_TABLE(h), 0, rb_obj_shape_slot_size(h) - sizeof(struct RHash));
|
||||
}
|
||||
|
||||
NOINLINE(static int ar_equal(VALUE x, VALUE y));
|
||||
@@ -757,7 +776,7 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
|
||||
st_hash_t hashes[RHASH_AR_TABLE_MAX_SIZE];
|
||||
unsigned int bound, size;
|
||||
|
||||
RUBY_ASSERT(rb_gc_obj_slot_size(hash) >= sizeof(struct RHash) + sizeof(ar_table));
|
||||
RUBY_ASSERT(rb_gc_obj_slot_size(hash) >= sizeof(struct RHash) + sizeof(st_table));
|
||||
|
||||
// prepare hash values
|
||||
while (1) {
|
||||
@@ -790,6 +809,28 @@ ar_force_convert_table(VALUE hash, const char *file, int line)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ar_compact_into(VALUE dst, VALUE src)
|
||||
{
|
||||
ar_table_pair *dst_pairs = RHASH_AR_TABLE(dst)->pairs;
|
||||
ar_table_pair *src_pairs = RHASH_AR_TABLE(src)->pairs;
|
||||
|
||||
const unsigned src_bound = RHASH_AR_TABLE_BOUND(src);
|
||||
const unsigned src_size = RHASH_AR_TABLE_SIZE(src);
|
||||
|
||||
unsigned j=0;
|
||||
for (unsigned i = 0; i < src_bound; i++) {
|
||||
if (!ar_cleared_entry(src, i)) {
|
||||
dst_pairs[j] = src_pairs[i];
|
||||
ar_hint_set_hint(dst, j, (st_hash_t)ar_hint(src, i));
|
||||
j++;
|
||||
}
|
||||
}
|
||||
RHASH_AR_TABLE_BOUND_SET(dst, src_size);
|
||||
RHASH_AR_TABLE_SIZE_SET(dst, src_size);
|
||||
hash_verify(dst);
|
||||
}
|
||||
|
||||
static int
|
||||
ar_compact_table(VALUE hash)
|
||||
{
|
||||
@@ -1040,11 +1081,9 @@ ar_update(VALUE hash, st_data_t key,
|
||||
}
|
||||
|
||||
static int
|
||||
ar_insert(VALUE hash, st_data_t key, st_data_t value)
|
||||
ar_insert_direct(VALUE hash, st_data_t key, st_data_t value, st_hash_t hash_value)
|
||||
{
|
||||
unsigned bin = RHASH_AR_TABLE_BOUND(hash);
|
||||
st_hash_t hash_value = ar_do_hash(key);
|
||||
|
||||
if (UNLIKELY(!RHASH_AR_TABLE_P(hash))) {
|
||||
// `#hash` changes ar_table -> st_table
|
||||
return -1;
|
||||
@@ -1074,6 +1113,13 @@ ar_insert(VALUE hash, st_data_t key, st_data_t value)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ar_insert(VALUE hash, st_data_t key, st_data_t value)
|
||||
{
|
||||
st_hash_t hash_value = ar_do_hash(key);
|
||||
return ar_insert_direct(hash, key, value, hash_value);
|
||||
}
|
||||
|
||||
static int
|
||||
ar_lookup(VALUE hash, st_data_t key, st_data_t *value)
|
||||
{
|
||||
@@ -1199,16 +1245,21 @@ ar_values(VALUE hash, st_data_t *values, st_index_t size)
|
||||
static ar_table*
|
||||
ar_copy(VALUE hash1, VALUE hash2)
|
||||
{
|
||||
RUBY_ASSERT(rb_gc_obj_slot_size(hash1) >= sizeof(struct RHash) + sizeof(ar_table));
|
||||
ar_table *old_tab = RHASH_AR_TABLE(hash2);
|
||||
RUBY_ASSERT(rb_gc_obj_slot_size(hash1) >= ar_memsize(RHASH_SIZE(hash2)));
|
||||
ar_table *new_tab = RHASH_AR_TABLE(hash1);
|
||||
|
||||
unsigned int bound = RHASH_AR_TABLE_BOUND(hash2);
|
||||
unsigned int size = RHASH_AR_TABLE_SIZE(hash2);
|
||||
if (UNLIKELY(bound != size)) {
|
||||
ar_compact_into(hash1, hash2);
|
||||
return new_tab;
|
||||
}
|
||||
|
||||
ar_table *old_tab = RHASH_AR_TABLE(hash2);
|
||||
new_tab->ar_hint.word = old_tab->ar_hint.word;
|
||||
MEMCPY(&new_tab->pairs, &old_tab->pairs, ar_table_pair, bound);
|
||||
RHASH_AR_TABLE_BOUND_SET(hash1, bound);
|
||||
RHASH_AR_TABLE_SIZE_SET(hash1, RHASH_AR_TABLE_SIZE(hash2));
|
||||
|
||||
rb_gc_writebarrier_remember(hash1);
|
||||
|
||||
return new_tab;
|
||||
@@ -1490,16 +1541,19 @@ compact_after_delete(VALUE hash)
|
||||
static inline size_t
|
||||
hash_slot_size(size_t capa, bool frozen)
|
||||
{
|
||||
const size_t st_size = sizeof(struct RHash) + sizeof(st_table);
|
||||
if (capa > RHASH_AR_TABLE_MAX_SIZE) {
|
||||
return sizeof(struct RHash) + sizeof(st_table);
|
||||
return st_size;
|
||||
}
|
||||
|
||||
const size_t ar_size = ar_memsize(capa);
|
||||
// If the hash is immutable, we can allocate a slot with exactly as much space as needed.
|
||||
if (frozen) {
|
||||
return sizeof(struct RHash) + offsetof(ar_table, pairs) + capa * sizeof(ar_table_pair);
|
||||
// But if mutable, we must ensure we have enough space to transition to an st_table.
|
||||
if (frozen || ar_size >= st_size) {
|
||||
return ar_size;
|
||||
}
|
||||
|
||||
return sizeof(struct RHash) + sizeof(ar_table);
|
||||
return st_size;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
@@ -1524,6 +1578,9 @@ hash_init_capa(VALUE hash, size_t size)
|
||||
if (size > RHASH_AR_TABLE_MAX_SIZE) {
|
||||
hash_st_table_init(hash, &objhash, size);
|
||||
}
|
||||
else {
|
||||
RUBY_ASSERT(RHASH_AR_TABLE_MAX_BOUND(hash) >= size);
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
@@ -1545,15 +1602,6 @@ rb_hash_alloc_copy(VALUE klass, VALUE src)
|
||||
return hash_alloc_capa(klass, RHASH_SIZE(src));
|
||||
}
|
||||
|
||||
#if USE_ZJIT
|
||||
size_t
|
||||
rb_zjit_hash_new_size(VALUE *flags_out)
|
||||
{
|
||||
*flags_out = T_HASH;
|
||||
return hash_slot_size(0, false);
|
||||
}
|
||||
#endif
|
||||
|
||||
static VALUE
|
||||
empty_hash_alloc(VALUE klass)
|
||||
{
|
||||
@@ -1598,14 +1646,32 @@ rb_hash_alloc_fixed_size(VALUE klass, st_index_t size)
|
||||
return hash_init_capa(hash_alloc(klass, 0, Qnil, size, true), size);
|
||||
}
|
||||
|
||||
static int
|
||||
ar_add_direct_i(st_data_t key, st_data_t value, st_data_t hash_value, st_data_t arg)
|
||||
{
|
||||
VALUE ret = (VALUE)arg;
|
||||
ar_insert_direct(ret, key, value, hash_value);
|
||||
return ST_CONTINUE;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
hash_copy(VALUE ret, VALUE hash)
|
||||
{
|
||||
RUBY_ASSERT(RHASH_SIZE(ret) == 0);
|
||||
if (RHASH_ST_TABLE_P(ret)) {
|
||||
RUBY_ASSERT(RHASH_ST_TABLE(ret)->entries == NULL);
|
||||
RHASH_UNSET_ST_FLAG(ret);
|
||||
}
|
||||
|
||||
if (rb_hash_compare_by_id_p(hash)) {
|
||||
// If `hash` is an ar_table it can't be `compare_by_identity?`.
|
||||
RUBY_ASSERT(RHASH_ST_TABLE_P(hash));
|
||||
RHASH_SET_ST_FLAG(ret);
|
||||
rb_gc_register_pinning_obj(ret);
|
||||
}
|
||||
else if (RHASH_AR_TABLE_MAX_BOUND(ret) < RHASH_SIZE(hash)) {
|
||||
RHASH_SET_ST_FLAG(ret);
|
||||
}
|
||||
|
||||
if (RHASH_AR_TABLE_P(hash)) {
|
||||
if (RHASH_AR_TABLE_P(ret)) {
|
||||
@@ -1613,11 +1679,7 @@ hash_copy(VALUE ret, VALUE hash)
|
||||
}
|
||||
else {
|
||||
st_table *tab = RHASH_ST_TABLE(ret);
|
||||
|
||||
// If `hash` is an ar_table it can't be `compare_by_identity?`.
|
||||
RUBY_ASSERT(!rb_hash_compare_by_id_p(hash));
|
||||
RUBY_ASSERT(RHASH_ST_TABLE(ret)->entries == NULL);
|
||||
st_init_existing_table_with_size(RHASH_ST_TABLE(ret), &objhash, RHASH_SIZE(hash));
|
||||
st_init_existing_table_with_size(tab, &objhash, RHASH_SIZE(hash));
|
||||
|
||||
int bound = RHASH_AR_TABLE_BOUND(hash);
|
||||
for (int i = 0; i < bound; i++) {
|
||||
@@ -1631,9 +1693,13 @@ hash_copy(VALUE ret, VALUE hash)
|
||||
}
|
||||
}
|
||||
else {
|
||||
RHASH_SET_ST_FLAG(ret);
|
||||
st_replace(RHASH_ST_TABLE(ret), RHASH_ST_TABLE(hash));
|
||||
rb_gc_writebarrier_remember(ret);
|
||||
if (RHASH_AR_TABLE_P(ret)) {
|
||||
rb_st_foreach_with_hash(RHASH_ST_TABLE(hash), ar_add_direct_i, (st_data_t)ret);
|
||||
}
|
||||
else {
|
||||
st_replace(RHASH_ST_TABLE(ret), RHASH_ST_TABLE(hash));
|
||||
rb_gc_writebarrier_remember(ret);
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -1663,6 +1729,9 @@ hash_dup_capa(VALUE hash, size_t capa)
|
||||
if (capa > RHASH_AR_TABLE_MAX_SIZE) {
|
||||
RHASH_SET_ST_FLAG(ret);
|
||||
}
|
||||
else {
|
||||
RUBY_ASSERT(RHASH_AR_TABLE_MAX_BOUND(ret) >= capa);
|
||||
}
|
||||
hash_copy(ret, hash);
|
||||
return ret;
|
||||
}
|
||||
@@ -1685,6 +1754,14 @@ rb_hash_resurrect(VALUE hash)
|
||||
}
|
||||
|
||||
#if USE_ZJIT
|
||||
size_t
|
||||
rb_zjit_hash_new_size(VALUE *flags_out, size_t size)
|
||||
{
|
||||
RUBY_ASSERT(size <= RHASH_AR_TABLE_MAX_SIZE);
|
||||
*flags_out = T_HASH;
|
||||
return hash_slot_size(size, false);
|
||||
}
|
||||
|
||||
bool
|
||||
rb_zjit_hash_dup_can_fastpath(VALUE hash, size_t *alloc_size_out, VALUE *flags_out, VALUE *ifnone_out, long *bound_out)
|
||||
{
|
||||
@@ -1693,7 +1770,7 @@ rb_zjit_hash_dup_can_fastpath(VALUE hash, size_t *alloc_size_out, VALUE *flags_o
|
||||
|
||||
const unsigned int bound = RHASH_AR_TABLE_BOUND(hash);
|
||||
|
||||
*alloc_size_out = hash_slot_size(0, false);
|
||||
*alloc_size_out = hash_slot_size(bound, false);
|
||||
*flags_out = T_HASH
|
||||
| ((VALUE)RHASH_AR_TABLE_SIZE(hash) << RHASH_AR_TABLE_SIZE_SHIFT)
|
||||
| ((VALUE)bound << RHASH_AR_TABLE_BOUND_SHIFT);
|
||||
@@ -1859,7 +1936,7 @@ rb_hash_init(rb_execution_context_t *ec, VALUE hash, VALUE capa_value, VALUE ifn
|
||||
|
||||
if (capa_value != INT2FIX(0)) {
|
||||
long capa = NUM2LONG(capa_value);
|
||||
if (capa > RHASH_AR_TABLE_MAX_SIZE && RHASH_SIZE(hash) == 0 && RHASH_AR_TABLE_P(hash)) {
|
||||
if (capa > 0 && RHASH_AR_TABLE_P(hash) && RHASH_SIZE(hash) == 0 && capa > RHASH_AR_TABLE_MAX_BOUND(hash)) {
|
||||
hash_st_table_init(hash, &objhash, capa);
|
||||
}
|
||||
}
|
||||
@@ -5248,7 +5325,7 @@ rb_hash_bulk_insert(long argc, const VALUE *argv, VALUE hash)
|
||||
st_index_t size = argc / 2;
|
||||
|
||||
if (RHASH_AR_TABLE_P(hash) &&
|
||||
(RHASH_AR_TABLE_SIZE(hash) + size <= RHASH_AR_TABLE_MAX_SIZE)) {
|
||||
(RHASH_AR_TABLE_SIZE(hash) + size <= RHASH_AR_TABLE_MAX_BOUND(hash))) {
|
||||
ar_bulk_insert(hash, argc, argv);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -89,6 +89,7 @@ bool rb_hash_default_unredefined(VALUE hash);
|
||||
VALUE rb_hash_alloc_fixed_size(VALUE klass, st_index_t size);
|
||||
VALUE rb_ident_hash_new_capa(long size);
|
||||
void rb_hash_free(VALUE hash);
|
||||
VALUE rb_hash_alloc_copy(VALUE klass, VALUE src);
|
||||
RUBY_EXTERN VALUE rb_cHash_empty_frozen;
|
||||
|
||||
static inline unsigned RHASH_AR_TABLE_SIZE_RAW(VALUE h);
|
||||
|
||||
@@ -20,4 +20,7 @@ void rb_st_free_embedded_table(st_table *tab);
|
||||
int rb_st_insert_no_rebuild(st_table *tab, st_data_t key, st_data_t value);
|
||||
#define st_insert_no_rebuild rb_st_insert_no_rebuild
|
||||
|
||||
typedef int st_foreach_with_hash_callback_func(st_data_t, st_data_t, st_data_t, st_data_t);
|
||||
int rb_st_foreach_with_hash(st_table *, st_foreach_with_hash_callback_func *, st_data_t);
|
||||
#define st_foreach_with_hash rb_st_foreach_with_hash
|
||||
#endif
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "internal/eval.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/inits.h"
|
||||
#include "internal/hash.h"
|
||||
#include "internal/numeric.h"
|
||||
#include "internal/object.h"
|
||||
#include "internal/struct.h"
|
||||
@@ -612,7 +613,16 @@ rb_obj_dup(VALUE obj)
|
||||
if (special_object_p(obj)) {
|
||||
return obj;
|
||||
}
|
||||
dup = rb_obj_alloc(rb_obj_class(obj));
|
||||
|
||||
switch (OBJ_BUILTIN_TYPE(obj)) {
|
||||
case T_HASH:
|
||||
dup = rb_hash_alloc_copy(rb_obj_class(obj), obj);
|
||||
break;
|
||||
default:
|
||||
dup = rb_obj_alloc(rb_obj_class(obj));
|
||||
break;
|
||||
}
|
||||
|
||||
return rb_obj_dup_setup(obj, dup);
|
||||
}
|
||||
|
||||
|
||||
@@ -1719,6 +1719,57 @@ st_general_foreach(st_table *tab, st_foreach_check_callback_func *func, st_updat
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef INTERNAL_ST_H
|
||||
int
|
||||
st_foreach_with_hash(st_table *tab, st_foreach_with_hash_callback_func *func, st_data_t arg)
|
||||
{
|
||||
st_table_entry *entries, *curr_entry_ptr;
|
||||
enum st_retval retval;
|
||||
st_index_t i, rebuilds_num;
|
||||
st_hash_t hash;
|
||||
st_data_t key;
|
||||
int packed_p = !st_has_bins(tab);
|
||||
|
||||
entries = tab->entries;
|
||||
/* The bound can change inside the loop even without rebuilding
|
||||
the table, e.g. by an entry insertion. */
|
||||
for (i = tab->entries_start; i < tab->entries_bound; i++) {
|
||||
curr_entry_ptr = &entries[i];
|
||||
if (EXPECT(DELETED_ENTRY_P(curr_entry_ptr), 0))
|
||||
continue;
|
||||
key = curr_entry_ptr->key;
|
||||
rebuilds_num = tab->rebuilds_num;
|
||||
hash = curr_entry_ptr->hash;
|
||||
retval = (*func)(key, curr_entry_ptr->record, hash, arg);
|
||||
|
||||
if (rebuilds_num != tab->rebuilds_num) {
|
||||
retry:
|
||||
entries = tab->entries;
|
||||
packed_p = !st_has_bins(tab);
|
||||
if (packed_p) {
|
||||
i = find_entry(tab, hash, key);
|
||||
if (EXPECT(i == REBUILT_TABLE_ENTRY_IND, 0))
|
||||
goto retry;
|
||||
}
|
||||
else {
|
||||
i = find_table_entry_ind(tab, hash, key);
|
||||
if (EXPECT(i == REBUILT_TABLE_ENTRY_IND, 0))
|
||||
goto retry;
|
||||
i -= ENTRY_BASE;
|
||||
}
|
||||
curr_entry_ptr = &entries[i];
|
||||
}
|
||||
switch (retval) {
|
||||
case ST_STOP:
|
||||
return 0;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
int
|
||||
st_foreach_with_replace(st_table *tab, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg)
|
||||
{
|
||||
|
||||
+15
-2
@@ -1958,10 +1958,23 @@ class TestHashOnly < Test::Unit::TestCase
|
||||
end
|
||||
end
|
||||
obj.hash_calls = 0
|
||||
hash = {obj => 42}
|
||||
|
||||
ar_hash = {obj => 42}
|
||||
assert_equal(1, obj.hash_calls)
|
||||
yield hash
|
||||
yield ar_hash
|
||||
assert_equal(1, obj.hash_calls)
|
||||
|
||||
st_hash = {a:1, b:2, c:3, d:4, e:5, f:6, g:7, h:8, obj => 42}
|
||||
assert_equal(2, obj.hash_calls)
|
||||
yield st_hash
|
||||
assert_equal(2, obj.hash_calls)
|
||||
|
||||
st_hash.keys.first(8).each do |key|
|
||||
st_hash.delete(key)
|
||||
end
|
||||
assert_equal(2, obj.hash_calls)
|
||||
yield st_hash
|
||||
assert_equal(2, obj.hash_calls)
|
||||
end
|
||||
|
||||
def test_select_reject_will_not_rehash
|
||||
|
||||
@@ -132,7 +132,7 @@ void rb_zjit_invalidate_root_box(void);
|
||||
void rb_zjit_jit_frame_update_references(zjit_jit_frame_t *jit_frame);
|
||||
void rb_zjit_materialize_frames(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
|
||||
void rb_zjit_materialize_frames_for_longjmp(const rb_execution_context_t *ec, rb_control_frame_t *cfp);
|
||||
size_t rb_zjit_hash_new_size(VALUE *flags_out);
|
||||
size_t rb_zjit_hash_new_size(VALUE *flags_out, size_t size);
|
||||
VALUE rb_zjit_new_obj_shape(VALUE flags, size_t alloc_size);
|
||||
bool rb_zjit_class_allocate_instance_fastpath(VALUE klass, size_t *size_out, VALUE *flags_out);
|
||||
bool rb_zjit_str_resurrect_fastpath(VALUE str, bool chilled, size_t *size_out, VALUE *flags_out, long *len_out, size_t *byte_size_out);
|
||||
|
||||
+2
-2
@@ -2546,7 +2546,7 @@ fn gen_new_hash(
|
||||
gen_prepare_leaf_call_with_gc(asm, state);
|
||||
|
||||
let mut flags = VALUE(0);
|
||||
let alloc_size = unsafe { rb_zjit_hash_new_size(&mut flags) };
|
||||
let alloc_size = unsafe { rb_zjit_hash_new_size(&mut flags, 0) };
|
||||
let klass = unsafe { rb_cHash };
|
||||
|
||||
gc_fastpath::gc_fastpath_new_obj(jit, asm, function, state, alloc_size, flags.into(), klass,
|
||||
@@ -2565,7 +2565,7 @@ fn gen_new_hash(
|
||||
let num_pairs = elements.len() / 2;
|
||||
let hash = if num_pairs <= RUBY_RHASH_AR_TABLE_MAX_SIZE as usize {
|
||||
let mut flags = VALUE(0);
|
||||
let alloc_size = unsafe { rb_zjit_hash_new_size(&mut flags) };
|
||||
let alloc_size = unsafe { rb_zjit_hash_new_size(&mut flags, num_pairs) };
|
||||
let klass = unsafe { rb_cHash };
|
||||
|
||||
gc_fastpath::gc_fastpath_new_obj(jit, asm, function, state, alloc_size, flags.into(), klass,
|
||||
|
||||
Generated
+1
-1
@@ -2392,7 +2392,7 @@ unsafe extern "C" {
|
||||
pub fn rb_iseq_label(iseq: *const rb_iseq_t) -> VALUE;
|
||||
pub fn rb_iseq_defined_string(type_: defined_type) -> VALUE;
|
||||
pub fn rb_zjit_profile_enable(iseq: *const rb_iseq_t);
|
||||
pub fn rb_zjit_hash_new_size(flags_out: *mut VALUE) -> usize;
|
||||
pub fn rb_zjit_hash_new_size(flags_out: *mut VALUE, size: usize) -> usize;
|
||||
pub fn rb_zjit_new_obj_shape(flags: VALUE, alloc_size: usize) -> VALUE;
|
||||
pub fn rb_zjit_class_allocate_instance_fastpath(
|
||||
klass: VALUE,
|
||||
|
||||
Reference in new issue
Block a user