ruby-changes:57807
From: =E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3 <ko1@a...>
Date: Thu, 19 Sep 2019 15:18:36 +0900 (JST)
Subject: [ruby-changes:57807] d74fa8e55c (master): reuse cc->call
https://git.ruby-lang.org/ruby.git/commit/?id=d74fa8e55c From d74fa8e55ce64904f2f99dfef4cbdff94e290672 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=8D=9C=E9=83=A8=E6=98=8C=E5=B9=B3?= <shyouhei@r...> Date: Wed, 18 Sep 2019 17:18:48 +0900 Subject: reuse cc->call I noticed that in case of cache misshit, re-calculated cc->me can be the same method entry than the pevious one. That is an okay situation but can't we partially reuse the cache, because cc->call should still be valid then? One thing that has to be special-cased is when the method entry gets amended by some refinements. That happens behind-the-scene of call cache mechanism. We have to check if cc->me->def points to the previously saved one. Calculating ------------------------------------- trunk ours vm2_poly_same_method 1.534M 2.025M i/s - 6.000M times in 3.910203s 2.962752s Comparison: vm2_poly_same_method ours: 2025143.9 i/s trunk: 1534447.2 i/s - 1.32x slower diff --git a/benchmark/vm2_poly_same_method.yml b/benchmark/vm2_poly_same_method.yml new file mode 100644 index 0000000..867c433 --- /dev/null +++ b/benchmark/vm2_poly_same_method.yml @@ -0,0 +1,25 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/vm2_poly_same_method.yml#L1 +prelude: | + module AR; end + class AR::Base + def create_or_update + nil + end + def save + create_or_update + end + end + class Foo < AR::Base; end + class Bar < AR::Base; end + o1 = Foo.new + o2 = Bar.new +benchmark: + vm2_poly_same_method: | + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; + o1.save; o2.save; +loop_count: 6000000 diff --git a/insns.def b/insns.def index e3edc5e..f365106 100644 --- a/insns.def +++ b/insns.def @@ -911,7 +911,7 @@ invokeblock https://github.com/ruby/ruby/blob/trunk/insns.def#L911 // attr rb_snum_t sp_inc = sp_inc_of_invokeblock(ci); { static struct rb_call_cache cc = { - 0, 0, NULL, vm_invokeblock_i, + 0, 0, NULL, NULL, vm_invokeblock_i, }; VALUE bh = VM_BLOCK_HANDLER_NONE; diff --git a/internal.h b/internal.h index 3dbe9ad..2e8d23b 100644 --- a/internal.h +++ b/internal.h @@ -2328,6 +2328,7 @@ enum method_missing_reason { https://github.com/ruby/ruby/blob/trunk/internal.h#L2328 MISSING_NONE = 0x40 }; struct rb_callable_method_entry_struct; +struct rb_method_definition_struct; struct rb_execution_context_struct; struct rb_control_frame_struct; struct rb_calling_info; @@ -2339,6 +2340,7 @@ struct rb_call_cache { https://github.com/ruby/ruby/blob/trunk/internal.h#L2340 /* inline cache: values */ const struct rb_callable_method_entry_struct *me; + const struct rb_method_definition_struct *def; VALUE (*call)(struct rb_execution_context_struct *ec, struct rb_control_frame_struct *cfp, diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 352d38f..66c50cd 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1374,16 +1374,41 @@ vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1374 static VALUE vm_call_general(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc); +#ifdef __has_attribute +#if __has_attribute(artificial) +__attribute__((__artificial__)) +#endif +#endif +static inline vm_call_handler +calccall(const struct rb_call_cache *cc, const rb_callable_method_entry_t *me) +{ + if (UNLIKELY(!me)) { + return vm_call_general; /* vm_call_method_nome() situation */ + } + else if (LIKELY(cc->me != me)) { + return vm_call_general; /* normal cases */ + } + else if (UNLIKELY(cc->def != me->def)) { + return vm_call_general; /* cc->me was refined elsewhere */ + } + else { + return cc->call; + } +} + MJIT_FUNC_EXPORTED void rb_vm_search_method_slowpath(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE klass) { - cc->me = rb_callable_method_entry(klass, ci->mid); + const rb_callable_method_entry_t *me = + rb_callable_method_entry(klass, ci->mid); + *cc = (struct rb_call_cache) { + GET_GLOBAL_METHOD_STATE(), + RCLASS_SERIAL(klass), + me, + me ? me->def : NULL, + calccall(cc, me), + }; VM_ASSERT(callable_method_entry_p(cc->me)); - cc->call = vm_call_general; -#if OPT_INLINE_METHOD_CACHE - cc->method_state = GET_GLOBAL_METHOD_STATE(); - cc->class_serial = RCLASS_SERIAL(klass); -#endif } static void -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/