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

ruby-changes:60767

From: Takashi <ko1@a...>
Date: Tue, 14 Apr 2020 12:33:22 +0900 (JST)
Subject: [ruby-changes:60767] 310ef9f40b (master): Make vm_call_cfunc_with_frame a fastpath (#3027)

https://git.ruby-lang.org/ruby.git/commit/?id=310ef9f40b

From 310ef9f40b350957a1a6d6236ce453ff9f73e81d Mon Sep 17 00:00:00 2001
From: Takashi Kokubun <takashikkbn@g...>
Date: Mon, 13 Apr 2020 20:32:59 -0700
Subject: Make vm_call_cfunc_with_frame a fastpath (#3027)

when there's no need to call CALLER_SETUP_ARG and CALLER_REMOVE_EMPTY_KW_SPLAT
(i.e. !rb_splat_or_kwargs_p(ci) && !calling->kw_splat).

Micro benchmark:
```
$ benchmark-driver -v --rbenv 'before;after' benchmark/vm_send_cfunc.yml --repeat-count=4
before: ruby 2.8.0dev (2020-04-13T23:45:05Z master b9d3ceee8f) [x86_64-linux]
after: ruby 2.8.0dev (2020-04-14T00:48:52Z no-splat-fastpath 418d363722) [x86_64-linux]
Calculating -------------------------------------
                         before       after
       vm_send_cfunc    69.585M     88.724M i/s -    100.000M times in 1.437097s 1.127096s

Comparison:
                    vm_send_cfunc
               after:  88723605.2 i/s
              before:  69584737.1 i/s - 1.28x  slower
```

Optcarrot:
```
$ benchmark-driver -v --rbenv 'before;after' benchmark.yml --repeat-count=12 --output=all
before: ruby 2.8.0dev (2020-04-13T23:45:05Z master b9d3ceee8f) [x86_64-linux]
after: ruby 2.8.0dev (2020-04-14T00:48:52Z no-splat-fastpath 418d363722) [x86_64-linux]
Calculating -------------------------------------
                                       before                 after
Optcarrot Lan_Master.nes    50.76119601545175     42.73858236484051 fps
                            50.76388649761503     51.04211379912850
                            50.80930672252514     51.39455790755538
                            50.90236000778749     51.75656936556145
                            51.01744746340430     51.86875277356489
                            51.06495279015112     51.88692482485558
                            51.07785337168974     51.93429603190578
                            51.20163525187862     51.95768145071314
                            51.34671771913112     52.45577266040274
                            51.35918340835583     52.53163888762858
                            51.46641337418146     52.62172484121034
                            51.50835463462257     52.85064021113239
```

diff --git a/benchmark/vm_send_cfunc.yml b/benchmark/vm_send_cfunc.yml
new file mode 100644
index 0000000..b114ac3
--- /dev/null
+++ b/benchmark/vm_send_cfunc.yml
@@ -0,0 +1,3 @@ https://github.com/ruby/ruby/blob/trunk/benchmark/vm_send_cfunc.yml#L1
+benchmark:
+  vm_send_cfunc: self.class
+loop_count: 100000000
diff --git a/debug_counter.h b/debug_counter.h
index 62b3cea..4e4c993 100644
--- a/debug_counter.h
+++ b/debug_counter.h
@@ -66,6 +66,7 @@ RB_DEBUG_COUNTER(ccf_iseq_opt) /* has_opt == TRUE (has optional parameters), but https://github.com/ruby/ruby/blob/trunk/debug_counter.h#L66
 RB_DEBUG_COUNTER(ccf_iseq_kw1) /* vm_call_iseq_setup_kwparm_kwarg() */
 RB_DEBUG_COUNTER(ccf_iseq_kw2) /* vm_call_iseq_setup_kwparm_nokwarg() */
 RB_DEBUG_COUNTER(ccf_cfunc)
+RB_DEBUG_COUNTER(ccf_cfunc_with_frame)
 RB_DEBUG_COUNTER(ccf_ivar) /* attr_reader */
 RB_DEBUG_COUNTER(ccf_attrset) /* attr_writer */
 RB_DEBUG_COUNTER(ccf_method_missing)
diff --git a/tool/ruby_vm/views/_mjit_compile_send.erb b/tool/ruby_vm/views/_mjit_compile_send.erb
index 255648b..2b1ecdb 100644
--- a/tool/ruby_vm/views/_mjit_compile_send.erb
+++ b/tool/ruby_vm/views/_mjit_compile_send.erb
@@ -18,9 +18,12 @@ https://github.com/ruby/ruby/blob/trunk/tool/ruby_vm/views/_mjit_compile_send.erb#L18
 % # compiler: Inline send insn where some supported fastpath is used.
     const rb_iseq_t *iseq = NULL;
     const CALL_INFO ci = cd->ci;
+    int kw_splat = IS_ARGS_KW_SPLAT(ci) > 0;
+    extern bool rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci);
     if (!status->compile_info->disable_send_cache && has_valid_method_type(captured_cc) && (
-%       # `CC_SET_FASTPATH(cc, vm_call_cfunc, TRUE)` in `vm_call_method_each_type`
-        vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC
+%       # `CC_SET_FASTPATH(cd->cc, vm_call_cfunc_with_frame, ...)` in `vm_call_cfunc`
+        (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC
+         && !rb_splat_or_kwargs_p(ci) && !kw_splat)
 %       # `CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(...), vm_call_iseq_optimizable_p(...))` in `vm_callee_setup_arg`,
 %       # and support only non-VM_CALL_TAILCALL path inside it
         || (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_ISEQ
@@ -61,14 +64,14 @@ https://github.com/ruby/ruby/blob/trunk/tool/ruby_vm/views/_mjit_compile_send.erb#L64
 % else
             fprintf(f, "        calling.block_handler = VM_BLOCK_HANDLER_NONE;\n");
 % end
-            fprintf(f, "        calling.kw_splat = %d;\n", IS_ARGS_KW_SPLAT(ci) > 0);
+            fprintf(f, "        calling.kw_splat = %d;\n", kw_splat);
             fprintf(f, "        calling.recv = stack[%d];\n", b->stack_size + sp_inc - 1);
             fprintf(f, "        calling.argc = %d;\n", vm_ci_argc(ci));
 
             if (vm_cc_cme(captured_cc)->def->type == VM_METHOD_TYPE_CFUNC) {
 %               # TODO: optimize this more
                 fprintf(f, "        CALL_DATA cd = (CALL_DATA)0x%"PRIxVALUE";\n", operands[0]);
-                fprintf(f, "        val = vm_call_cfunc(ec, reg_cfp, &calling, cd);\n");
+                fprintf(f, "        val = vm_call_cfunc_with_frame(ec, reg_cfp, &calling, cd);\n");
             }
             else { // VM_METHOD_TYPE_ISEQ
 %               # fastpath_applied_iseq_p checks rb_simple_iseq_p, which ensures has_opt == FALSE
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 21b5e93..a4edd9a 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1964,6 +1964,13 @@ rb_iseq_only_kwparam_p(const rb_iseq_t *iseq) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1964
            iseq->body->param.flags.has_block == FALSE;
 }
 
+// If true, cc->call needs to include `CALLER_SETUP_ARG` (i.e. can't be skipped in fastpath)
+MJIT_STATIC bool
+rb_splat_or_kwargs_p(const struct rb_callinfo *restrict ci)
+{
+    return IS_ARGS_SPLAT(ci) || IS_ARGS_KW_OR_KW_SPLAT(ci);
+}
+
 
 static inline void
 CALLER_SETUP_ARG(struct rb_control_frame_struct *restrict cfp,
@@ -2508,6 +2515,7 @@ vm_method_cfunc_entry(const rb_callable_method_entry_t *me) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L2515
 static VALUE
 vm_call_cfunc_with_frame(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, struct rb_call_data *cd)
 {
+    RB_DEBUG_COUNTER_INC(ccf_cfunc_with_frame);
     const struct rb_callinfo *ci = cd->ci;
     const struct rb_callcache *cc = cd->cc;
     VALUE val;
@@ -2555,6 +2563,7 @@ vm_call_cfunc(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L2563
 
     CALLER_SETUP_ARG(reg_cfp, calling, ci);
     CALLER_REMOVE_EMPTY_KW_SPLAT(reg_cfp, calling, ci);
+    CC_SET_FASTPATH(cd->cc, vm_call_cfunc_with_frame, !rb_splat_or_kwargs_p(ci) && !calling->kw_splat);
     return vm_call_cfunc_with_frame(ec, reg_cfp, calling, cd);
 }
 
-- 
cgit v0.10.2


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

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