aboutsummaryrefslogtreecommitdiffstats
path: root/test/ruby
diff options
context:
space:
mode:
authorPeter Zhu <peter@peterzhu.ca>2023-07-31 09:24:48 -0400
committerPeter Zhu <peter@peterzhu.ca>2023-07-31 11:46:53 -0400
commitb98838b65cbf8fa2d228f9e0249414cba4e3197d (patch)
tree342e39f04c293d898f0d0e7d1d2997df30cb8fe7 /test/ruby
parent1bda22f5fedea5364c6df321a3004c47c732144e (diff)
downloadruby-b98838b65cbf8fa2d228f9e0249414cba4e3197d.tar.gz
Store initial slots per size pool
This commit stores the initial slots per size pool, configured with the environment variables `RUBY_GC_HEAP_INIT_SIZE_%d_SLOTS`. This ensures that the configured initial slots remains a low bound for the number of slots in the heap, which can prevent heaps from thrashing in size.
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/test_gc.rb54
1 files changed, 54 insertions, 0 deletions
diff --git a/test/ruby/test_gc.rb b/test/ruby/test_gc.rb
index 640c5c6340..811ab3edad 100644
--- a/test/ruby/test_gc.rb
+++ b/test/ruby/test_gc.rb
@@ -399,6 +399,60 @@ class TestGc < Test::Unit::TestCase
end
end
+ def test_gc_parameter_init_slots
+ assert_separately(["--disable-gems"], __FILE__, __LINE__, <<~RUBY)
+ # Constant from gc.c.
+ GC_HEAP_INIT_SLOTS = 10_000
+ GC.stat_heap.each do |_, s|
+ # Sometimes pages will have 1 less slot due to alignment, so always increase slots_per_page by 1.
+ slots_per_page = (s[:heap_eden_slots] / s[:heap_eden_pages]) + 1
+ total_slots = s[:heap_eden_slots] + s[:heap_allocatable_pages] * slots_per_page
+ # Give a 0.9x delta because integer division in minimum_pages_for_size_pool can sometimes cause number to be
+ # less than GC_HEAP_INIT_SLOTS.
+ assert_operator(total_slots, :>=, GC_HEAP_INIT_SLOTS * 0.9, s)
+ end
+ RUBY
+
+ env = {}
+ # Make the heap big enough to ensure the heap never needs to grow.
+ sizes = GC.stat_heap.keys.reverse.map { |i| (i + 1) * 100_000 }
+ GC.stat_heap.each do |i, s|
+ env["RUBY_GC_HEAP_INIT_SIZE_#{s[:slot_size]}_SLOTS"] = sizes[i].to_s
+ end
+ assert_separately([env, "-W0", "--disable-gems"], __FILE__, __LINE__, <<~RUBY)
+ SIZES = #{sizes}
+ GC.stat_heap.each do |i, s|
+ # Sometimes pages will have 1 less slot due to alignment, so always increase slots_per_page by 1.
+ slots_per_page = (s[:heap_eden_slots] / s[:heap_eden_pages]) + 1
+ total_slots = s[:heap_eden_slots] + s[:heap_allocatable_pages] * slots_per_page
+ assert_in_epsilon(SIZES[i], total_slots, 0.01, s)
+ end
+ RUBY
+
+ # Check that the configured sizes are "remembered" across GC invocations.
+ assert_separately([env, "-W0", "--disable-gems"], __FILE__, __LINE__, <<~RUBY)
+ SIZES = #{sizes}
+
+ # Fill size pool 0 with transient objects.
+ ary = []
+ while GC.stat_heap(0, :heap_allocatable_pages) != 0
+ ary << Object.new
+ end
+ ary = nil
+
+ # Clear all the objects that were allocated.
+ GC.start
+
+ # Check that we still have the same number of slots as initially configured.
+ GC.stat_heap.each do |i, s|
+ # Sometimes pages will have 1 less slot due to alignment, so always increase slots_per_page by 1.
+ slots_per_page = (s[:heap_eden_slots] / s[:heap_eden_pages]) + 1
+ total_slots = s[:heap_eden_slots] + s[:heap_allocatable_pages] * slots_per_page
+ assert_in_epsilon(SIZES[i], total_slots, 0.01, s)
+ end
+ RUBY
+ end
+
def test_profiler_enabled
GC::Profiler.enable
assert_equal(true, GC::Profiler.enabled?)