mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Improve the performance of super
This PR improves the performance of `super` calls. While working on some Rails optimizations jhawthorn discovered that `super` calls were slower than expected. The changes here do the following: 1) Adds a check for whether the call frame is not equal to the method entry iseq. This avoids the `rb_obj_is_kind_of` check on the next line which is quite slow. If the current call frame is equal to the method entry we know we can't have an instance eval, etc. 2) Changes `FL_TEST` to `FL_TEST_RAW`. This is safe because we've already done the check for `T_ICLASS` above. 3) Adds a benchmark for `T_ICLASS` super calls. 4) Note: makes a chage for `method_entry_cref` to use `const`. On master the benchmarks showed that `super` is 1.76x slower. Our changes improved the performance so that it is now only 1.36x slower. Benchmark IPS: ``` Warming up -------------------------------------- super 244.918k i/100ms method call 383.007k i/100ms Calculating ------------------------------------- super 2.280M (± 6.7%) i/s - 11.511M in 5.071758s method call 3.834M (± 4.9%) i/s - 19.150M in 5.008444s Comparison: method call: 3833648.3 i/s super: 2279837.9 i/s - 1.68x (± 0.00) slower ``` With changes: ``` Warming up -------------------------------------- super 308.777k i/100ms method call 375.051k i/100ms Calculating ------------------------------------- super 2.951M (± 5.4%) i/s - 14.821M in 5.039592s method call 3.551M (± 4.9%) i/s - 18.002M in 5.081695s Comparison: method call: 3551372.7 i/s super: 2950557.9 i/s - 1.20x (± 0.00) slower ``` Ruby VM benchmarks also showed an improvement: Existing `vm_super` benchmark`. ``` $ make benchmark ITEM=vm_super | |compare-ruby|built-ruby| |:---------|-----------:|---------:| |vm_super | 21.555M| 37.819M| | | -| 1.75x| ``` New `vm_iclass_super` benchmark: ``` $ make benchmark ITEM=vm_iclass_super | |compare-ruby|built-ruby| |:----------------|-----------:|---------:| |vm_iclass_super | 1.669M| 3.683M| | | -| 2.21x| ``` This is the benchmark script used for the benchmark-ips benchmarks: ```ruby require "benchmark/ips" class Foo def zuper; end def top; end last_method = "top" ("A".."M").each do |module_name| eval <<-EOM module #{module_name} def zuper; super; end def #{module_name.downcase} #{last_method} end end prepend #{module_name} EOM last_method = module_name.downcase end end foo = Foo.new Benchmark.ips do |x| x.report "super" do foo.zuper end x.report "method call" do foo.m end x.compare! end ``` Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org> Co-authored-by: John Hawthorn <john@hawthorn.email>
This commit is contained in:
parent
b717f73402
commit
637d1cc0c0
Notes:
git
2020-09-24 03:53:11 +09:00
2 changed files with 34 additions and 2 deletions
20
benchmark/vm_iclass_super.yml
Normal file
20
benchmark/vm_iclass_super.yml
Normal file
|
@ -0,0 +1,20 @@
|
|||
prelude: |
|
||||
class C
|
||||
def m
|
||||
1
|
||||
end
|
||||
|
||||
("A".."M").each do |module_name|
|
||||
eval <<-EOM
|
||||
module #{module_name}
|
||||
def m; super; end
|
||||
end
|
||||
prepend #{module_name}
|
||||
EOM
|
||||
end
|
||||
end
|
||||
|
||||
obj = C.new
|
||||
benchmark:
|
||||
vm_iclass_super: obj.m
|
||||
loop_count: 6000000
|
|
@ -673,8 +673,19 @@ rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
|
|||
return check_method_entry(ep[VM_ENV_DATA_INDEX_ME_CREF], TRUE);
|
||||
}
|
||||
|
||||
static rb_iseq_t *
|
||||
method_entry_iseqptr(const rb_callable_method_entry_t *me)
|
||||
{
|
||||
switch (me->def->type) {
|
||||
case VM_METHOD_TYPE_ISEQ:
|
||||
return me->def->body.iseq.iseqptr;
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static rb_cref_t *
|
||||
method_entry_cref(rb_callable_method_entry_t *me)
|
||||
method_entry_cref(const rb_callable_method_entry_t *me)
|
||||
{
|
||||
switch (me->def->type) {
|
||||
case VM_METHOD_TYPE_ISEQ:
|
||||
|
@ -3263,7 +3274,7 @@ static inline VALUE
|
|||
vm_search_normal_superclass(VALUE klass)
|
||||
{
|
||||
if (BUILTIN_TYPE(klass) == T_ICLASS &&
|
||||
FL_TEST(RBASIC(klass)->klass, RMODULE_IS_REFINEMENT)) {
|
||||
FL_TEST_RAW(RBASIC(klass)->klass, RMODULE_IS_REFINEMENT)) {
|
||||
klass = RBASIC(klass)->klass;
|
||||
}
|
||||
klass = RCLASS_ORIGIN(klass);
|
||||
|
@ -3296,6 +3307,7 @@ vm_search_super_method(const rb_control_frame_t *reg_cfp, struct rb_call_data *c
|
|||
|
||||
if (BUILTIN_TYPE(current_defined_class) != T_MODULE &&
|
||||
!FL_TEST(current_defined_class, RMODULE_INCLUDED_INTO_REFINEMENT) &&
|
||||
reg_cfp->iseq != method_entry_iseqptr(me) &&
|
||||
!rb_obj_is_kind_of(recv, current_defined_class)) {
|
||||
VALUE m = RB_TYPE_P(current_defined_class, T_ICLASS) ?
|
||||
RCLASS_INCLUDER(current_defined_class) : current_defined_class;
|
||||
|
|
Loading…
Reference in a new issue