[前][次][番号順一覧][スレッド一覧]

ruby-changes:72515

From: Matt <ko1@a...>
Date: Tue, 12 Jul 2022 21:50:55 +0900 (JST)
Subject: [ruby-changes:72515] 214ed4cbc6 (master): [Feature #18901] Support size pool movement for Arrays

https://git.ruby-lang.org/ruby.git/commit/?id=214ed4cbc6

From 214ed4cbc6f33675230602dd09268b436da96f7d Mon Sep 17 00:00:00 2001
From: Matt Valentine-House <matt@e...>
Date: Thu, 9 Jun 2022 15:59:08 +0100
Subject: [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
---
 array.c                      | 42 +++++++++++++++++++++++++++++++++++++++++-
 gc.c                         | 25 ++++++++++++++++++-------
 internal/array.h             |  3 +++
 test/ruby/test_gc_compact.rb | 40 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 102 insertions(+), 8 deletions(-)

diff --git a/array.c b/array.c
index 7b3f5bd0b0..dc8c4abe07 100644
--- a/array.c
+++ b/array.c
@@ -107,7 +107,6 @@ should_not_be_shared_and_embedded(VALUE ary) https://github.com/ruby/ruby/blob/trunk/array.c#L107
 #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) https://github.com/ruby/ruby/blob/trunk/array.c#L211
 #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) https://github.com/ruby/ruby/blob/trunk/array.c#L491
 }
 #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)
 {
diff --git a/gc.c b/gc.c
index 4741cb7b04..39d82a6a77 100644
--- a/gc.c
+++ b/gc.c
@@ -8324,16 +8324,20 @@ gc_compact_destination_pool(rb_objspace_t *objspace, rb_size_pool_t *src_pool, V https://github.com/ruby/ruby/blob/trunk/gc.c#L8324
     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) https://github.com/ruby/ruby/blob/trunk/gc.c#L10372
         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:
diff --git a/internal/array.h b/internal/array.h
index 60f66f31bf..690196a1e2 100644
--- a/internal/array.h
+++ b/internal/array.h
@@ -30,6 +30,9 @@ size_t rb_ary_memsize(VALUE); https://github.com/ruby/ruby/blob/trunk/internal/array.h#L30
 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);
diff --git a/test/ruby/test_gc_compact.rb b/test/ruby/test_gc_compact.rb
index 3927958e9c..8ecb41438a 100644
--- a/test/ruby/test_gc_compact.rb
+++ b/test/ruby/test_gc_compact.rb
@@ -209,6 +209,46 @@ class TestGCCompact < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_gc_compact.rb#L209
     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;
-- 
cgit v1.2.1


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]