summaryrefslogtreecommitdiff
path: root/test/ruby
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2024-05-10 12:51:30 -0700
committerJeremy Evans <code@jeremyevans.net>2024-05-21 05:33:57 +0900
commitf021bcdbbe69d324b36c03dab4d2347608658b86 (patch)
tree8b4e11f3e9f0649692331c81d7fca8102217a20a /test/ruby
parent7c2c528d350cb5497a2358bdb5684c4822b0f239 (diff)
Add allocation tests for ruby2_keywords
This tests ruby2_keywords flagged methods, as well as passing ruby2_keywords flagged hashes to other methods. Some of the behavior here is questionable, such as allocating different numbers of objects depending on whether a block is passed or whether YJIT is enabled. I think there are likely ways to eliminate allocations in certain cases. However, this gives us a baseline and shows us where it is possible to make improvements.
Diffstat (limited to 'test/ruby')
-rw-r--r--test/ruby/test_allocation.rb121
1 files changed, 121 insertions, 0 deletions
diff --git a/test/ruby/test_allocation.rb b/test/ruby/test_allocation.rb
index 48348c0fbd..8f6858f1cf 100644
--- a/test/ruby/test_allocation.rb
+++ b/test/ruby/test_allocation.rb
@@ -25,6 +25,10 @@ class TestAllocation < Test::Unit::TestCase
empty_array = empty_array = []
empty_hash = empty_hash = {}
array1 = array1 = [1]
+ r2k_array = r2k_array = [Hash.ruby2_keywords_hash(a: 3)]
+ r2k_array1 = r2k_array1 = [1, Hash.ruby2_keywords_hash(a: 3)]
+ r2k_empty_array = r2k_empty_array = [Hash.ruby2_keywords_hash({})]
+ r2k_empty_array1 = r2k_empty_array1 = [1, Hash.ruby2_keywords_hash({})]
hash1 = hash1 = {a: 2}
nill = nill = nil
block = block = lambda{}
@@ -95,6 +99,8 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 0, "none(*empty_array, *empty_array#{block})")
check_allocations(0, 1, "none(**empty_hash, **empty_hash#{block})")
check_allocations(1, 1, "none(*empty_array, *empty_array, **empty_hash, **empty_hash#{block})")
+
+ check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "none(*r2k_empty_array#{block})")
RUBY
end
@@ -114,6 +120,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "required(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "required(*array1, *empty_array, **empty_hash#{block})")
+ check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "required(*r2k_empty_array1#{block})")
+ check_allocations(0, 1, "required(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required(*empty_array, **hash1, **empty_hash#{block})")
RUBY
@@ -135,6 +144,10 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "optional(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "optional(*array1, *empty_array, **empty_hash#{block})")
+ check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "optional(*r2k_empty_array#{block})")
+ check_allocations(#{block.empty?} ? 0 : 1, #{block.empty?} ? 0 : 1, "optional(*r2k_empty_array1#{block})")
+ check_allocations(0, 1, "optional(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "optional(*empty_array, **hash1, **empty_hash#{block})")
RUBY
@@ -166,6 +179,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat(*empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat(*r2k_array#{block})")
+ check_allocations(1, 1, "splat(*r2k_array1#{block})")
RUBY
end
@@ -195,6 +213,10 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "req_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "req_splat(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "req_splat(*empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "req_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "req_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "req_splat(*r2k_array1#{block})")
RUBY
end
@@ -224,6 +246,10 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat_post(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat_post(*array1, *empty_array, **empty_hash#{block})")
check_allocations(1, 1, "splat_post(*empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "splat_post(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat_post(*r2k_array#{block})")
+ check_allocations(1, 1, "splat_post(*r2k_array1#{block})")
RUBY
end
@@ -251,6 +277,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "keyword(**hash1, **empty_hash#{block})")
check_allocations(1, 0, "keyword(*empty_array, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "keyword(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword(*empty_array, **hash1, **empty_hash#{block})")
@@ -281,6 +310,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "keyword_splat(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
@@ -311,6 +343,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(0, 1, "keyword_and_keyword_splat(**hash1, **empty_hash#{block})")
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, *empty_array, **empty_hash#{block})")
+ check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "keyword_and_keyword_splat(*r2k_array#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "keyword_and_keyword_splat(*empty_array, **hash1, **empty_hash#{block})")
@@ -350,6 +385,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(*array1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "required_and_keyword(*r2k_array1#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required_and_keyword(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword(1, *empty_array, **hash1, **empty_hash#{block})")
@@ -397,6 +435,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat_and_keyword(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "splat_and_keyword(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*r2k_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat_and_keyword(*r2k_array1#{block})")
RUBY
end
@@ -433,6 +476,9 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(*array1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "required_and_keyword_splat(*r2k_array1#{block})")
+
# Currently allocates 1 array unnecessarily due to splatarray true
check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, a: 2, **empty_hash#{block})")
check_allocations(1, 1, "required_and_keyword_splat(1, *empty_array, **hash1, **empty_hash#{block})")
@@ -480,6 +526,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 1, "splat_and_keyword_splat(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "splat_and_keyword_splat(*r2k_array1#{block})")
RUBY
end
@@ -521,6 +572,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array1#{block})")
RUBY
end
@@ -562,6 +618,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "anon_splat_and_anon_keyword_splat(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "anon_splat_and_anon_keyword_splat(*r2k_array1#{block})")
RUBY
end
@@ -603,6 +664,11 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})")
RUBY
end
@@ -644,6 +710,61 @@ class TestAllocation < Test::Unit::TestCase
check_allocations(1, 1, "argument_forwarding(*array1, **empty_hash, a: 2#{block})")
check_allocations(1, 1, "argument_forwarding(*array1, **hash1, **empty_hash#{block})")
check_allocations(1, 0, "argument_forwarding(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "argument_forwarding(*r2k_array1#{block})")
+ RUBY
+ end
+
+ def test_ruby2_keywords
+ check_allocations(<<~RUBY)
+ def self.r2k(*a#{block}); end
+ singleton_class.send(:ruby2_keywords, :r2k)
+
+ check_allocations(1, 1, "r2k(1, a: 2#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, a: 2#{block})")
+ check_allocations(1, 1, "r2k(1, a:2, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, **empty_hash, a: 2#{block})")
+
+ check_allocations(1, 0, "r2k(1, **nil#{block})")
+ check_allocations(1, 1, "r2k(1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, **hash1#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, **hash1#{block})")
+ check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, **empty_hash, **hash1#{block})")
+
+ check_allocations(1, 0, "r2k(1, *empty_array#{block})")
+ check_allocations(1, 1, "r2k(1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "r2k(*array1, a: 2#{block})")
+
+ check_allocations(1, 0, "r2k(*array1, **nill#{block})")
+ check_allocations(1, 1, "r2k(*array1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(*array1, **hash1#{block})")
+ check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1#{block})")
+
+ check_allocations(1, 0, "r2k(*array1, *empty_array#{block})")
+ check_allocations(1, 1, "r2k(*array1, *empty_array, **empty_hash#{block})")
+
+ check_allocations(1, 1, "r2k(*array1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(*array1, *empty_array, **hash1, **empty_hash#{block})")
+
+ check_allocations(1, 1, "r2k(1, *empty_array, a: 2, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(1, *empty_array, **hash1, **empty_hash#{block})")
+ check_allocations(1, 1, "r2k(*array1, **empty_hash, a: 2#{block})")
+ check_allocations(1, 1, "r2k(*array1, **hash1, **empty_hash#{block})")
+ check_allocations(1, 0, "r2k(*array1, **nil#{block})")
+
+ check_allocations(1, 1, "r2k(*r2k_empty_array#{block})")
+ check_allocations(1, 1, "r2k(*r2k_array#{block})")
+ unless defined?(RubyVM::YJIT.enabled?) && RubyVM::YJIT.enabled?
+ # YJIT may or may not allocate depending on arch?
+ check_allocations(1, 1, "r2k(*r2k_empty_array1#{block})")
+ check_allocations(1, 1, "r2k(*r2k_array1#{block})")
+ end
RUBY
end