[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]