diff options
author | Koichi Sasada <ko1@atdot.net> | 2023-07-31 16:17:55 +0900 |
---|---|---|
committer | Koichi Sasada <ko1@atdot.net> | 2023-07-31 17:13:43 +0900 |
commit | cfd7729ce7a31c8b6ec5dd0e99c67b2932de4732 (patch) | |
tree | 3b489657b66d4a67e179afddb2dd950f8892bcd0 /vm_insnhelper.c | |
parent | 280419d0e0ba3e96e19551c70cba789fbedd80e1 (diff) |
use inline cache for refinements
From Ruby 3.0, refined method invocations are slow because
resolved methods are not cached by inline cache because of
conservertive strategy. However, `using` clears all caches
so that it seems safe to cache resolved method entries.
This patch caches resolved method entries in inline cache
and clear all of inline method caches when `using` is called.
fix [Bug #18572]
```ruby
# without refinements
class C
def foo = :C
end
N = 1_000_000
obj = C.new
require 'benchmark'
Benchmark.bm{|x|
x.report{N.times{
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
}}
}
_END__
user system total real
master 0.362859 0.002544 0.365403 ( 0.365424)
modified 0.357251 0.000000 0.357251 ( 0.357258)
```
```ruby
# with refinment but without using
class C
def foo = :C
end
module R
refine C do
def foo = :R
end
end
N = 1_000_000
obj = C.new
require 'benchmark'
Benchmark.bm{|x|
x.report{N.times{
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
}}
}
__END__
user system total real
master 0.957182 0.000000 0.957182 ( 0.957212)
modified 0.359228 0.000000 0.359228 ( 0.359238)
```
```ruby
# with using
class C
def foo = :C
end
module R
refine C do
def foo = :R
end
end
N = 1_000_000
using R
obj = C.new
require 'benchmark'
Benchmark.bm{|x|
x.report{N.times{
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
obj.foo; obj.foo; obj.foo; obj.foo; obj.foo;
}}
}
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/8129
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r-- | vm_insnhelper.c | 19 |
1 files changed, 14 insertions, 5 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 5e52207d81..6291e13fec 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -2003,6 +2003,8 @@ vm_ccs_verify(struct rb_class_cc_entries *ccs, ID mid, VALUE klass) VM_ASSERT(IMEMO_TYPE_P(cc, imemo_callcache)); VM_ASSERT(vm_cc_class_check(cc, klass)); VM_ASSERT(vm_cc_check_cme(cc, ccs->cme)); + VM_ASSERT(!vm_cc_super_p(cc)); + VM_ASSERT(!vm_cc_refinement_p(cc)); } return TRUE; } @@ -4193,12 +4195,19 @@ search_refined_method(rb_execution_context_t *ec, rb_control_frame_t *cfp, struc static VALUE vm_call_refined(rb_execution_context_t *ec, rb_control_frame_t *cfp, struct rb_calling_info *calling) { - struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, - search_refined_method(ec, cfp, calling)); + const rb_callable_method_entry_t *ref_cme = search_refined_method(ec, cfp, calling); - if (vm_cc_cme(ref_cc)) { - calling->cc= ref_cc; - return vm_call_method(ec, cfp, calling); + if (ref_cme) { + if (calling->cd->cc) { + const struct rb_callcache *cc = calling->cc = vm_cc_new(vm_cc_cme(calling->cc)->defined_class, ref_cme, vm_call_general, cc_type_refinement); + RB_OBJ_WRITE(cfp->iseq, &calling->cd->cc, cc); + return vm_call_method(ec, cfp, calling); + } + else { + struct rb_callcache *ref_cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, ref_cme); + calling->cc= ref_cc; + return vm_call_method(ec, cfp, calling); + } } else { return vm_call_method_nome(ec, cfp, calling); |