aboutsummaryrefslogtreecommitdiffstats
path: root/vm_insnhelper.c
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-04-02 11:06:13 -0700
committerJeremy Evans <code@jeremyevans.net>2023-04-25 08:06:16 -0700
commit583e9d24d419023bc1123190768297a468113613 (patch)
treec585901a2b7fef9726398d6175a1c5e00eb4eee7 /vm_insnhelper.c
parent9b4bf02aa89fa9a9a568b7be045ab1df8053f0e6 (diff)
downloadruby-583e9d24d419023bc1123190768297a468113613.tar.gz
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.
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r--vm_insnhelper.c45
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);
}