mirror of
https://github.com/ruby/ruby.git
synced 2025-08-15 13:39:04 +02:00
Struct: keep direct reference to IMEMO/fields when space allows
It's not rare for structs to have additional ivars, hence are one of the most common, if not the most common type in the `gen_fields_tbl`. This can cause Ractor contention, but even in single ractor mode means having to do a hash lookup to access the ivars, and increase GC work. Instead, unless the struct is perfectly right sized, we can store a reference to the associated IMEMO/fields object right after the last struct member. ``` compare-ruby: ruby 3.5.0dev (2025-08-06T12:50:36Z struct-ivar-fields-2 9a30d141a1) +PRISM [arm64-darwin24] built-ruby: ruby 3.5.0dev (2025-08-06T12:57:59Z struct-ivar-fields-2 2ff3ec237f) +PRISM [arm64-darwin24] warming up..... | |compare-ruby|built-ruby| |:---------------------|-----------:|---------:| |member_reader | 590.317k| 579.246k| | | 1.02x| -| |member_writer | 543.963k| 527.104k| | | 1.03x| -| |member_reader_method | 213.540k| 213.004k| | | 1.00x| -| |member_writer_method | 192.657k| 191.491k| | | 1.01x| -| |ivar_reader | 403.993k| 569.915k| | | -| 1.41x| ``` Co-Authored-By: Étienne Barrié <etienne.barrie@gmail.com>
This commit is contained in:
parent
9b3ad3449b
commit
f3206cc79b
8 changed files with 194 additions and 23 deletions
|
@ -252,3 +252,52 @@ class TestObjectIdRactor < Test::Unit::TestCase
|
|||
end;
|
||||
end
|
||||
end
|
||||
|
||||
class TestObjectIdStruct < TestObjectId
|
||||
EmbeddedStruct = Struct.new(:embedded_field)
|
||||
|
||||
def setup
|
||||
@obj = EmbeddedStruct.new
|
||||
end
|
||||
end
|
||||
|
||||
class TestObjectIdStructGenIvar < TestObjectId
|
||||
GenIvarStruct = Struct.new(:a, :b, :c)
|
||||
|
||||
def setup
|
||||
@obj = GenIvarStruct.new
|
||||
end
|
||||
end
|
||||
|
||||
class TestObjectIdStructNotEmbed < TestObjectId
|
||||
MANY_IVS = 80
|
||||
|
||||
StructNotEmbed = Struct.new(*MANY_IVS.times.map { |i| :"field_#{i}" })
|
||||
|
||||
def setup
|
||||
@obj = StructNotEmbed.new
|
||||
end
|
||||
end
|
||||
|
||||
class TestObjectIdStructTooComplex < TestObjectId
|
||||
StructTooComplex = Struct.new(:a) do
|
||||
def initialize
|
||||
@too_complex_obj_id_test = 1
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
if defined?(RubyVM::Shape::SHAPE_MAX_VARIATIONS)
|
||||
assert_equal 8, RubyVM::Shape::SHAPE_MAX_VARIATIONS
|
||||
end
|
||||
8.times do |i|
|
||||
StructTooComplex.new.instance_variable_set("@TestObjectIdStructTooComplex#{i}", 1)
|
||||
end
|
||||
@obj = StructTooComplex.new
|
||||
@obj.instance_variable_set("@a#{rand(10_000)}", 1)
|
||||
|
||||
if defined?(RubyVM::Shape)
|
||||
assert_predicate(RubyVM::Shape.of(@obj), :too_complex?)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -99,6 +99,24 @@ class TestRactor < Test::Unit::TestCase
|
|||
RUBY
|
||||
end
|
||||
|
||||
def test_struct_instance_variables
|
||||
assert_ractor(<<~'RUBY')
|
||||
StructIvar = Struct.new(:member) do
|
||||
def initialize(*)
|
||||
super
|
||||
@ivar = "ivar"
|
||||
end
|
||||
attr_reader :ivar
|
||||
end
|
||||
obj = StructIvar.new("member")
|
||||
obj_copy = Ractor.new { Ractor.receive }.send(obj).value
|
||||
assert_equal obj.ivar, obj_copy.ivar
|
||||
refute_same obj.ivar, obj_copy.ivar
|
||||
assert_equal obj.member, obj_copy.member
|
||||
refute_same obj.member, obj_copy.member
|
||||
RUBY
|
||||
end
|
||||
|
||||
def test_fork_raise_isolation_error
|
||||
assert_ractor(<<~'RUBY')
|
||||
ractor = Ractor.new do
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue