Skip to content

Commit

Permalink
Use a generic getter with __callee__
Browse files Browse the repository at this point in the history
This modifies the getter definition to use a single method that
uses `__callee__` to access the hash.

def __get_callee__!
  @table[__callee__]
end

On versions of Ruby 2.0+ and JRuby 9.4.2+ __callee__ will return
the aliased name, which in this case is the key for the table.

This eliminates one of the Proc-based methods per field and also
performs better on CRuby:

Before:

Warming up --------------------------------------
                 get    44.000  i/100ms
                 set    46.000  i/100ms
Calculating -------------------------------------
                 get    504.731  (± 8.5%) i/s -      2.508k in   5.024439s
                 set    506.779  (± 1.4%) i/s -      2.576k in   5.084061s

After:

Warming up --------------------------------------
                 get    80.000  i/100ms
                 set    65.000  i/100ms
Calculating -------------------------------------
                 get    808.319  (± 0.7%) i/s -      4.080k in   5.047805s
                 set    662.678  (± 0.9%) i/s -      3.315k in   5.002857s

It also uses less memory; creating 100k OpenStruct in the same
way as the benchmark uses 205.2MB before and 164MB after.
  • Loading branch information
headius committed Aug 9, 2023
1 parent 018946d commit 5175a3e
Showing 1 changed file with 7 additions and 4 deletions.
11 changes: 7 additions & 4 deletions lib/ostruct.rb
Original file line number Diff line number Diff line change
Expand Up @@ -221,16 +221,15 @@ def marshal_dump # :nodoc:
#
def new_ostruct_member!(name) # :nodoc:
unless @table.key?(name) || is_method_protected!(name)
# use generic method with __callee__ for getter
singleton_class.alias_method(name, :__get_callee__!)

if defined?(::Ractor)
getter_proc = nil.instance_eval{ Proc.new { @table[name] } }
setter_proc = nil.instance_eval{ Proc.new {|x| @table[name] = x} }
::Ractor.make_shareable(getter_proc)
::Ractor.make_shareable(setter_proc)
else
getter_proc = Proc.new { @table[name] }
setter_proc = Proc.new {|x| @table[name] = x}
end
define_singleton_method!(name, &getter_proc)
define_singleton_method!("#{name}=", &setter_proc)
end
end
Expand All @@ -254,6 +253,10 @@ def new_ostruct_member!(name) # :nodoc:
end
end

def __get_callee__!
@table[__callee__]
end

def freeze
@table.freeze
super
Expand Down

0 comments on commit 5175a3e

Please sign in to comment.