diff options
author | Jeremy Evans <code@jeremyevans.net> | 2023-04-02 11:06:13 -0700 |
---|---|---|
committer | Jeremy Evans <code@jeremyevans.net> | 2023-04-25 08:06:16 -0700 |
commit | 583e9d24d419023bc1123190768297a468113613 (patch) | |
tree | c585901a2b7fef9726398d6175a1c5e00eb4eee7 /vm_insnhelper.c | |
parent | 9b4bf02aa89fa9a9a568b7be045ab1df8053f0e6 (diff) |
Optimize symproc calls
Similar to the bmethod/send optimization, this avoids using
CALLER_ARG_SPLAT if not necessary. As long as the receiver argument
can be shifted off, other arguments are passed through as-is.
This optimizes the following types of calls:
* symproc.(recv) ~5%
* symproc.(recv, *args) ~65% for args.length == 200
* symproc.(recv, *args, **kw) ~45% for args.length == 200
* symproc.(recv, **kw) ~30%
* symproc.(recv, kw: 1) ~100%
Note that empty argument splats do get slower with this approach,
by about 2-3%. This is probably because iseq argument setup is
slower for empty argument splats than CALLER_SETUP_ARG is. Other
than non-empty argument splats, other argument splats are faster,
with the speedup depending on the number of arguments.
The following types of calls are not optimized:
* symproc.(*args)
* symproc.(*args, **kw)
This is because the you cannot shift the receiver argument off
without first splatting the arg.
Notes
Notes:
Merged: https://github.com/ruby/ruby/pull/7522
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r-- | vm_insnhelper.c | 45 |
1 files changed, 30 insertions, 15 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c index 35f864e20d..362ac7dcb2 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -4836,20 +4836,37 @@ vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, MAYBE_UNUSED(bool is_lambda), VALUE block_handler) { VALUE symbol = VM_BH_TO_SYMBOL(block_handler); - CALLER_SETUP_ARG(reg_cfp, calling, ci, ALLOW_HEAP_ARGV); - int flags = 0; - if (UNLIKELY(calling->heap_argv)) { + int flags = vm_ci_flag(ci); + + if (UNLIKELY(!(flags & VM_CALL_ARGS_SIMPLE) && + ((calling->argc == 0) || + (calling->argc == 1 && (flags & (VM_CALL_ARGS_SPLAT | VM_CALL_KW_SPLAT))) || + (calling->argc == 2 && (flags & VM_CALL_ARGS_SPLAT) && (flags & VM_CALL_KW_SPLAT)) || + ((flags & VM_CALL_KWARG) && (vm_ci_kwarg(ci)->keyword_len == calling->argc))))) { + CALLER_SETUP_ARG(reg_cfp, calling, ci, ALLOW_HEAP_ARGV); + flags = 0; + if (UNLIKELY(calling->heap_argv)) { #if VM_ARGC_STACK_MAX < 0 - if (RARRAY_LEN(calling->heap_argv) < 1) { - rb_raise(rb_eArgError, "no receiver given"); - } + if (RARRAY_LEN(calling->heap_argv) < 1) { + rb_raise(rb_eArgError, "no receiver given"); + } #endif - calling->recv = rb_ary_shift(calling->heap_argv); - // Modify stack to avoid cfp consistency error - reg_cfp->sp++; - reg_cfp->sp[-1] = reg_cfp->sp[-2]; - reg_cfp->sp[-2] = calling->recv; - flags |= VM_CALL_ARGS_SPLAT; + calling->recv = rb_ary_shift(calling->heap_argv); + // Modify stack to avoid cfp consistency error + reg_cfp->sp++; + reg_cfp->sp[-1] = reg_cfp->sp[-2]; + reg_cfp->sp[-2] = calling->recv; + flags |= VM_CALL_ARGS_SPLAT; + } + else { + if (calling->argc < 1) { + rb_raise(rb_eArgError, "no receiver given"); + } + calling->recv = TOPN(--calling->argc); + } + if (calling->kw_splat) { + flags |= VM_CALL_KW_SPLAT; + } } else { if (calling->argc < 1) { @@ -4857,9 +4874,7 @@ vm_invoke_symbol_block(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, } calling->recv = TOPN(--calling->argc); } - if (calling->kw_splat) { - flags |= VM_CALL_KW_SPLAT; - } + return vm_call_symbol(ec, reg_cfp, calling, ci, symbol, flags); } |