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

ruby-changes:40173

From: ko1 <ko1@a...>
Date: Sat, 24 Oct 2015 02:53:47 +0900 (JST)
Subject: [ruby-changes:40173] ko1:r52254 (trunk): * vm_insnhelper.c: introduce new call handler for simple ISeqs.

ko1	2015-10-24 02:53:35 +0900 (Sat, 24 Oct 2015)

  New Revision: 52254

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=52254

  Log:
    * vm_insnhelper.c: introduce new call handler for simple ISeqs.
    
      vm_call_iseq_setup_normal_0start() is simple, however it has
      some loops/conditions depends on ISeq::param.size and
      ISeq::local_size (in vm_push_frame(), inlined into this function).
    
      There are many simple methods which has a few parameters and local
      variables. So that this patch introduces several special functions
      generated in vm_call_iseq_optimized.inc by
      tool/mk_call_iseq_optimized.rb.
    
      This script makes
        vm_call_iseq_setup_normal_0start_Xparams_Ylocals()
      where X is 0 to 3 and Y is 1 to 6 (as current setting).
      In this case, X * Y = 24 functions are created.
    
      These functions creates fast method dispatch by inlining
      vm_push_frame() with immediate params/locals sizes.
    
      On my laptop, we can have the following results.
    
      vm2_method*       1.083 (8.3% faster)
      vm2_poly_method*  0.961 (3.4% slower)
    
      It shows 8.3% faster for inner loop method dispatch (hit inline
      cache), but 3.4% slower when inline cache miss because we need
      to find a suitable call handler.
    
    * common.mk: add a rule for vm_call_iseq_optimized.inc.
    
    * tool/mk_call_iseq_optimized.rb: added.
    
    * vm.c: include vm_call_iseq_optimized.inc.

  Added files:
    trunk/tool/mk_call_iseq_optimized.rb
  Modified files:
    trunk/ChangeLog
    trunk/common.mk
    trunk/vm.c
    trunk/vm_insnhelper.c
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 52253)
+++ ChangeLog	(revision 52254)
@@ -1,3 +1,39 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Sat Oct 24 02:02:24 2015  Koichi Sasada  <ko1@a...>
+
+	* vm_insnhelper.c: introduce new call handler for simple ISeqs.
+
+	  vm_call_iseq_setup_normal_0start() is simple, however it has
+	  some loops/conditions depends on ISeq::param.size and
+	  ISeq::local_size (in vm_push_frame(), inlined into this function).
+
+	  There are many simple methods which has a few parameters and local
+	  variables. So that this patch introduces several special functions
+	  generated in vm_call_iseq_optimized.inc by
+	  tool/mk_call_iseq_optimized.rb.
+
+	  This script makes 
+	    vm_call_iseq_setup_normal_0start_Xparams_Ylocals()
+	  where X is 0 to 3 and Y is 1 to 6 (as current setting).
+	  In this case, X * Y = 24 functions are created.
+
+	  These functions creates fast method dispatch by inlining
+	  vm_push_frame() with immediate params/locals sizes.
+
+	  On my laptop, we can have the following results.
+
+	  vm2_method*       1.083 (8.3% faster)
+	  vm2_poly_method*  0.961 (3.4% slower)
+
+	  It shows 8.3% faster for inner loop method dispatch (hit inline
+	  cache), but 3.4% slower when inline cache miss because we need
+	  to find a suitable call handler.
+
+	* common.mk: add a rule for vm_call_iseq_optimized.inc.
+
+	* tool/mk_call_iseq_optimized.rb: added.
+
+	* vm.c: include vm_call_iseq_optimized.inc.
+
 Sat Oct 24 01:58:50 2015  Koichi Sasada  <ko1@a...>
 
 	* vm_core.h: define vm_call_handler.
Index: common.mk
===================================================================
--- common.mk	(revision 52253)
+++ common.mk	(revision 52254)
@@ -819,6 +819,10 @@ known_errors.inc: $(srcdir)/template/kno https://github.com/ruby/ruby/blob/trunk/common.mk#L819
 	$(ECHO) generating $@
 	$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb -c -o $@ $(srcdir)/template/known_errors.inc.tmpl $(srcdir)/defs/known_errors.def
 
+vm_call_iseq_optimized.inc: $(srcdir)/tool/mk_call_iseq_optimized.rb
+	$(ECHO) generating $@
+	$(Q) $(BASERUBY) $(srcdir)/tool/mk_call_iseq_optimized.rb > vm_call_iseq_optimized.inc
+
 $(MINIPRELUDE_C): $(COMPILE_PRELUDE)
 	$(ECHO) generating $@
 	$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb -I$(srcdir) -o $@ \
@@ -2391,6 +2395,7 @@ vm.$(OBJEXT): {$(VPATH)}thread_native.h https://github.com/ruby/ruby/blob/trunk/common.mk#L2395
 vm.$(OBJEXT): {$(VPATH)}vm.c
 vm.$(OBJEXT): {$(VPATH)}vm.h
 vm.$(OBJEXT): {$(VPATH)}vm.inc
+vm.$(OBJEXT): {$(VPATH)}vm_call_iseq_optimized.inc
 vm.$(OBJEXT): {$(VPATH)}vm_args.c
 vm.$(OBJEXT): {$(VPATH)}vm_core.h
 vm.$(OBJEXT): {$(VPATH)}vm_debug.h
@@ -2431,6 +2436,9 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}vm_bac https://github.com/ruby/ruby/blob/trunk/common.mk#L2436
 vm_backtrace.$(OBJEXT): {$(VPATH)}vm_core.h
 vm_backtrace.$(OBJEXT): {$(VPATH)}vm_debug.h
 vm_backtrace.$(OBJEXT): {$(VPATH)}vm_opts.h
+vm_call.$(OBJEXT): $(top_srcdir)/include/ruby.h
+vm_call.$(OBJEXT): {$(VPATH)}vm_core.h
+vm_call.$(OBJEXT): {$(VPATH)}vm_call_iseq_optimized.inc
 vm_dump.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
 vm_dump.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
 vm_dump.$(OBJEXT): $(CCAN_DIR)/list/list.h
Index: vm.c
===================================================================
--- vm.c	(revision 52253)
+++ vm.c	(revision 52254)
@@ -3111,3 +3111,5 @@ vm_collect_usage_register(int reg, int i https://github.com/ruby/ruby/blob/trunk/vm.c#L3111
 	(*ruby_vm_collect_usage_func_register)(reg, isset);
 }
 #endif
+
+#include "vm_call_iseq_optimized.inc" /* required from vm_insnhelper.c */
Index: vm_insnhelper.c
===================================================================
--- vm_insnhelper.c	(revision 52253)
+++ vm_insnhelper.c	(revision 52254)
@@ -1235,17 +1235,17 @@ vm_base_ptr(rb_control_frame_t *cfp) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1235
 
 #include "vm_args.c"
 
-static inline VALUE vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
-static inline VALUE vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
+static inline VALUE vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc, int param_size, int local_size);
+static inline VALUE vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc, int param_size, int local_size);
 static inline VALUE vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
-static VALUE vm_call_iseq_setup_normal_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
-static VALUE vm_call_iseq_setup_tailcall_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
-
 static VALUE vm_call_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
 static VALUE vm_call_method_nome(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
 static VALUE vm_call_method_each_type(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
 static inline VALUE vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
 
+typedef VALUE (*vm_call_handler)(struct rb_thread_struct *th, struct rb_control_frame_struct *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
+static vm_call_handler vm_call_iseq_setup_func(const struct rb_call_info *ci, const int param_size, const int local_size);
+
 static rb_method_definition_t *method_definition_create(rb_method_type_t type, ID mid);
 static void method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts);
 static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
@@ -1332,8 +1332,33 @@ vm_callee_setup_block_arg(rb_thread_t *t https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1332
     }
 }
 
+static const rb_iseq_t *
+def_iseq_ptr(rb_method_definition_t *def)
+{
+#if VM_CHECK_MODE > 0
+    if (def->type != VM_METHOD_TYPE_ISEQ) rb_bug("def_iseq_ptr: not iseq (%d)", def->type);
+#endif
+    return def->body.iseq.iseqptr;
+}
+
+static VALUE
+vm_call_iseq_setup_tailcall_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+{
+    return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, 0);
+}
+
+static VALUE
+vm_call_iseq_setup_normal_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+{
+    const rb_iseq_t *iseq = def_iseq_ptr(cc->me->def);
+    int param = iseq->body->param.size;
+    int local = iseq->body->local_size;
+    return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0, param, local);
+}
+
 static inline int
-vm_callee_setup_arg(rb_thread_t *th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const rb_iseq_t *iseq, VALUE *argv)
+vm_callee_setup_arg(rb_thread_t *th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
+		    const rb_iseq_t *iseq, VALUE *argv, int param_size, int local_size)
 {
     if (LIKELY(simple_iseq_p(iseq))) {
 	rb_control_frame_t *cfp = th->cfp;
@@ -1344,9 +1369,7 @@ vm_callee_setup_arg(rb_thread_t *th, str https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1369
 	    argument_arity_error(th, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
 	}
 
-	CI_SET_FASTPATH(cc,
-			(UNLIKELY(ci->flag & VM_CALL_TAILCALL) ? vm_call_iseq_setup_tailcall_0start :
-			                                         vm_call_iseq_setup_normal_0start),
+	CI_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size),
 			(!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
 			 !(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED)));
 	return 0;
@@ -1356,27 +1379,22 @@ vm_callee_setup_arg(rb_thread_t *th, str https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1379
     }
 }
 
-static const rb_iseq_t *
-def_iseq_ptr(rb_method_definition_t *def)
-{
-#if VM_CHECK_MODE > 0
-    if (def->type != VM_METHOD_TYPE_ISEQ) rb_bug("def_iseq_ptr: not iseq (%d)", def->type);
-#endif
-    return def->body.iseq.iseqptr;
-}
-
 static VALUE
 vm_call_iseq_setup(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
 {
-    int opt_pc = vm_callee_setup_arg(th, calling, ci, cc, def_iseq_ptr(cc->me->def), cfp->sp - calling->argc);
-    return vm_call_iseq_setup_2(th, cfp, calling, ci, cc, opt_pc);
+    const rb_iseq_t *iseq = def_iseq_ptr(cc->me->def);
+    const int param_size = iseq->body->param.size;
+    const int local_size = iseq->body->local_size;
+    const int opt_pc = vm_callee_setup_arg(th, calling, ci, cc, def_iseq_ptr(cc->me->def), cfp->sp - calling->argc, param_size, local_size);
+    return vm_call_iseq_setup_2(th, cfp, calling, ci, cc, opt_pc, param_size, local_size);
 }
 
 static inline VALUE
-vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc)
+vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
+		     int opt_pc, int param_size, int local_size)
 {
     if (LIKELY(!(ci->flag & VM_CALL_TAILCALL))) {
-	return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, opt_pc);
+	return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, opt_pc, param_size, local_size);
     }
     else {
 	return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, opt_pc);
@@ -1384,24 +1402,26 @@ vm_call_iseq_setup_2(rb_thread_t *th, rb https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1402
 }
 
 static inline VALUE
-vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc)
+vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
+			  int opt_pc, int param_size, int local_size)
 {
     const rb_callable_method_entry_t *me = cc->me;
     const rb_iseq_t *iseq = def_iseq_ptr(me->def);
     VALUE *argv = cfp->sp - calling->argc;
-    VALUE *sp = argv + iseq->body->param.size;
+    VALUE *sp = argv + param_size;
     cfp->sp = argv - 1 /* recv */;
 
     vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, calling->recv,
 		  VM_ENVVAL_BLOCK_PTR(calling->blockptr), (VALUE)me,
 		  iseq->body->iseq_encoded + opt_pc, sp,
-		  iseq->body->local_size - iseq->body->param.size,
+		  local_size - param_size,
 		  iseq->body->stack_max);
     return Qundef;
 }
 
 static inline VALUE
-vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc)
+vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
+			    int opt_pc)
 {
     unsigned int i;
     VALUE *argv = cfp->sp - calling->argc;
@@ -1437,18 +1457,6 @@ vm_call_iseq_setup_tailcall(rb_thread_t https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1457
 }
 
 static VALUE
-vm_call_iseq_setup_normal_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
-{
-    return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0);
-}
-
-static VALUE
-vm_call_iseq_setup_tailcall_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
-{
-    return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, 0);
-}
-
-static VALUE
 call_cfunc_m2(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *argv)
 {
     return (*func)(recv, rb_ary_new4(argc, argv));
Index: tool/mk_call_iseq_optimized.rb
===================================================================
--- tool/mk_call_iseq_optimized.rb	(revision 0)
+++ tool/mk_call_iseq_optimized.rb	(revision 52254)
@@ -0,0 +1,72 @@ https://github.com/ruby/ruby/blob/trunk/tool/mk_call_iseq_optimized.rb#L1
+
+puts <<EOS
+#if 1 /* enable or disable this optimization */
+
+/* DO NOT EDIT THIS FILE DIRECTLY
+ *
+ * This file is enerated by tool/mkcall_iseq.rb
+ */
+
+EOS
+
+P = (0..3)
+L = (1..6)
+
+def fname param, local
+  "vm_call_iseq_setup_normal_0start_#{param}params_#{local}locals"
+end
+    
+P.each{|param|
+  L.each{|local|
+    puts <<EOS
+static VALUE
+#{fname(param, local)}(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
+{
+    return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0, #{param}, #{local});
+}
+
+EOS
+    #
+  }
+}
+
+puts <<EOS
+/* vm_call_iseq_handlers[param][local] */
+static const vm_call_handler vm_call_iseq_handlers[][#{L.to_a.size}] = {
+#{P.map{|param| '{' + L.map{|local| fname(param, local)}.join(",\n ") + '}'}.join(",\n")}
+};
+
+static inline vm_call_handler
+vm_call_iseq_setup_func(const struct rb_call_info *ci, const int param_size, const int local_size)
+{
+    if (UNLIKELY(ci->flag & VM_CALL_TAILCALL)) {
+	return vm_call_iseq_setup_tailcall_0start;
+    }
+    else if (0) { /* to disable optimize */
+        return vm_call_iseq_setup_normal_0start;
+    }
+    else {
+	if (param_size <= #{P.end} &&
+	    local_size <= #{L.end}) {
+	    VM_ASSERT(local_size != 0);
+	    return vm_call_iseq_handlers[param_size][local_size-1];
+	}
+	return vm_call_iseq_setup_normal_0start;
+    }
+}
+
+#else
+
+
+static inline vm_call_handler
+vm_call_iseq_setup_func(const struct rb_call_info *ci, struct rb_call_cache *cc)
+{
+    if (UNLIKELY(ci->flag & VM_CALL_TAILCALL)) {
+	return vm_call_iseq_setup_tailcall_0start;
+    }
+    else {
+        return vm_call_iseq_setup_normal_0start;
+    }
+}
+#endif
+EOS

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

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