mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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 masterb9d3ceee8f
) [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 masterb9d3ceee8f
) [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 ```
This commit is contained in:
parent
b9d3ceee8f
commit
310ef9f40b
Notes:
git
2020-04-14 12:33:24 +09:00
Merged-By: k0kubun <takashikkbn@gmail.com>
4 changed files with 20 additions and 4 deletions
3
benchmark/vm_send_cfunc.yml
Normal file
3
benchmark/vm_send_cfunc.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
benchmark:
|
||||
vm_send_cfunc: self.class
|
||||
loop_count: 100000000
|
|
@ -66,6 +66,7 @@ RB_DEBUG_COUNTER(ccf_iseq_opt) /* has_opt == TRUE (has optional parameters), but
|
|||
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)
|
||||
|
|
|
@ -18,9 +18,12 @@
|
|||
% # 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 @@
|
|||
% 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
|
||||
|
|
|
@ -1964,6 +1964,13 @@ rb_iseq_only_kwparam_p(const rb_iseq_t *iseq)
|
|||
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)
|
|||
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
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue