mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
[Feature #18901] Support size pool movement for Arrays
This commit enables Arrays to move between size pools during compaction. This can occur if the array is mutated such that it would fit in a different size pool when embedded. The move is carried out in two stages: 1. The RVALUE is moved to a destination heap during object movement phase of compaction 2. The array data is re-embedded and the original buffer free'd if required. This happens during the update references step
This commit is contained in:
parent
0f8a0c5f37
commit
214ed4cbc6
Notes:
git
2022-07-12 21:51:02 +09:00
4 changed files with 102 additions and 8 deletions
42
array.c
42
array.c
|
@ -107,7 +107,6 @@ should_not_be_shared_and_embedded(VALUE ary)
|
|||
#define ARY_SET_EMBED_LEN(ary, n) do { \
|
||||
long tmp_n = (n); \
|
||||
assert(ARY_EMBED_P(ary)); \
|
||||
assert(!OBJ_FROZEN(ary)); \
|
||||
RBASIC(ary)->flags &= ~RARRAY_EMBED_LEN_MASK; \
|
||||
RBASIC(ary)->flags |= (tmp_n) << RARRAY_EMBED_LEN_SHIFT; \
|
||||
} while (0)
|
||||
|
@ -212,6 +211,30 @@ ary_embeddable_p(long capa)
|
|||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
rb_ary_embeddable_p(VALUE ary)
|
||||
{
|
||||
// if the array is shared or a shared root then it's not moveable
|
||||
return !(ARY_SHARED_P(ary) || ARY_SHARED_ROOT_P(ary));
|
||||
}
|
||||
|
||||
size_t
|
||||
rb_ary_size_as_embedded(VALUE ary)
|
||||
{
|
||||
size_t real_size;
|
||||
|
||||
if (ARY_EMBED_P(ary)) {
|
||||
real_size = ary_embed_size(ARY_EMBED_LEN(ary));
|
||||
}
|
||||
else if (rb_ary_embeddable_p(ary)) {
|
||||
real_size = ary_embed_size(ARY_HEAP_CAPA(ary));
|
||||
}
|
||||
else {
|
||||
real_size = sizeof(struct RString);
|
||||
}
|
||||
return real_size;
|
||||
}
|
||||
|
||||
|
||||
#if ARRAY_DEBUG
|
||||
#define ary_verify(ary) ary_verify_(ary, __FILE__, __LINE__)
|
||||
|
@ -468,6 +491,23 @@ rb_ary_detransient(VALUE ary)
|
|||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
rb_ary_make_embedded(VALUE ary)
|
||||
{
|
||||
assert(rb_ary_embeddable_p(ary));
|
||||
if (!ARY_EMBED_P(ary)) {
|
||||
VALUE *buf = RARRAY_PTR(ary);
|
||||
long len = RARRAY_LEN(ary);
|
||||
|
||||
FL_SET_EMBED(ary);
|
||||
ARY_SET_EMBED_LEN(ary, len);
|
||||
RARY_TRANSIENT_UNSET(ary);
|
||||
|
||||
memmove(RARRAY_PTR(ary), buf, len * sizeof(VALUE));
|
||||
ary_heap_free_ptr(ary, buf, len * sizeof(VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ary_resize_capa(VALUE ary, long capacity)
|
||||
{
|
||||
|
|
25
gc.c
25
gc.c
|
@ -8324,16 +8324,20 @@ gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, V
|
|||
switch (BUILTIN_TYPE(src)) {
|
||||
case T_STRING:
|
||||
obj_size = rb_str_size_as_embedded(src);
|
||||
if (rb_gc_size_allocatable_p(obj_size)){
|
||||
return &size_pools[size_pool_idx_for_size(obj_size)];
|
||||
}
|
||||
else {
|
||||
GC_ASSERT(!STR_EMBED_P(src));
|
||||
return &size_pools[0];
|
||||
}
|
||||
break;
|
||||
case T_ARRAY:
|
||||
obj_size = rb_ary_size_as_embedded(src);
|
||||
break;
|
||||
default:
|
||||
return src_pool;
|
||||
}
|
||||
|
||||
if (rb_gc_size_allocatable_p(obj_size)) {
|
||||
return &size_pools[size_pool_idx_for_size(obj_size)];
|
||||
}
|
||||
else {
|
||||
return &size_pools[0];
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -10368,6 +10372,13 @@ gc_update_object_references(rb_objspace_t *objspace, VALUE obj)
|
|||
else {
|
||||
gc_ref_update_array(objspace, obj);
|
||||
}
|
||||
#if USE_RVARGC
|
||||
if ((size_t)GET_HEAP_PAGE(obj)->slot_size >= rb_ary_size_as_embedded(obj)) {
|
||||
if (rb_ary_embeddable_p(obj)) {
|
||||
rb_ary_make_embedded(obj);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
|
||||
case T_HASH:
|
||||
|
|
|
@ -30,6 +30,9 @@ size_t rb_ary_memsize(VALUE);
|
|||
VALUE rb_to_array_type(VALUE obj);
|
||||
VALUE rb_to_array(VALUE obj);
|
||||
void rb_ary_cancel_sharing(VALUE ary);
|
||||
size_t rb_ary_size_as_embedded(VALUE ary);
|
||||
void rb_ary_make_embedded(VALUE ary);
|
||||
bool rb_ary_embeddable_p(VALUE ary);
|
||||
|
||||
static inline VALUE rb_ary_entry_internal(VALUE ary, long offset);
|
||||
static inline bool ARY_PTR_USING_P(VALUE ary);
|
||||
|
|
|
@ -209,6 +209,46 @@ class TestGCCompact < Test::Unit::TestCase
|
|||
assert_equal([:call, :line], results)
|
||||
end
|
||||
|
||||
def test_moving_arrays_down_size_pools
|
||||
omit if !GC.using_rvargc?
|
||||
assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
|
||||
begin;
|
||||
ARY_COUNT = 500
|
||||
|
||||
GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||
|
||||
arys = ARY_COUNT.times.map do
|
||||
ary = "abbbbbbbbbb".chars
|
||||
ary.uniq!
|
||||
end
|
||||
|
||||
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||
assert_operator(stats.dig(:moved_down, :T_ARRAY), :>=, ARY_COUNT)
|
||||
assert(arys) # warning: assigned but unused variable - arys
|
||||
end;
|
||||
end
|
||||
|
||||
def test_moving_arrays_up_size_pools
|
||||
omit if !GC.using_rvargc?
|
||||
assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
|
||||
begin;
|
||||
ARY_COUNT = 500
|
||||
|
||||
GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||
|
||||
ary = "hello".chars
|
||||
arys = ARY_COUNT.times.map do
|
||||
x = []
|
||||
ary.each { |e| x << e }
|
||||
x
|
||||
end
|
||||
|
||||
stats = GC.verify_compaction_references(expand_heap: true, toward: :empty)
|
||||
assert_operator(stats.dig(:moved_up, :T_ARRAY), :>=, ARY_COUNT)
|
||||
assert(arys) # warning: assigned but unused variable - arys
|
||||
end;
|
||||
end
|
||||
|
||||
def test_moving_strings_up_size_pools
|
||||
assert_separately([], "#{<<~"begin;"}\n#{<<~"end;"}", timeout: 10, signal: :SEGV)
|
||||
begin;
|
||||
|
|
Loading…
Reference in a new issue