summaryrefslogtreecommitdiff
path: root/vm_insnhelper.c
diff options
context:
space:
mode:
authorko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-03-20 19:57:39 +0000
committerko1 <ko1@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2019-03-20 19:57:39 +0000
commit24e03d7e268f5b89367d495ee2b4aac0b700e6e1 (patch)
tree7916c47e4d2cdeda91e5ca398abf5064f44d1616 /vm_insnhelper.c
parent139634a16fc5fa6779b08aeb89f40b289579c4bf (diff)
optimize method dispatch for lead/opt params.
There is a special optimization for "only lead parameters" method dispatch using specialized dispatcher functions `vm_call_iseq_setup_normal_0start...`. Other cases (opt, rest, post, ...) we don't use specialized dispatcher and call with `setup_parameters_complex` to satisfy Ruby's complex parameter specification. This commit introduce a specialize dispatcher for methods which use only lead and optional parameters. Two step improvements: (1) prepare "lead/opt" only check pass. It is to skip the `setup_parameters_complex` function. (2) introduce specialized dispatcher for only "lead/opt" parameters methods (vm_call_iseq_setup_normal_opt_start). With these improvements, we achieved good micro-benchmark results: With a method: `def opt2 a, b=nil; end` With the following binaries: clean-miniruby: unmodified trunk. opt_miniruby: apply step (1). opt_cc_miniruby: apply step (2). Result with benchmark-driver: opt2(1) opt_cc_miniruby: 42269409.1 i/s opt_miniruby: 36304428.3 i/s - 1.16x slower clean-miniruby: 25897409.5 i/s - 1.63x slower opt2(1, 2) opt_cc_miniruby: 45935145.7 i/s opt_miniruby: 40513196.9 i/s - 1.13x slower clean-miniruby: 29976057.6 i/s - 1.53x slower This improvement may be trivial (difficult to improve practical cases). However, this is enough small patch so I decide to introduce it. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@67315 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r--vm_insnhelper.c103
1 files changed, 90 insertions, 13 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index ea082b0f82..20ecda6fc1 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1703,6 +1703,17 @@ rb_simple_iseq_p(const rb_iseq_t *iseq)
iseq->body->param.flags.has_block == FALSE;
}
+bool
+rb_iseq_only_optparam_p(const rb_iseq_t *iseq)
+{
+ return iseq->body->param.flags.has_opt == TRUE &&
+ iseq->body->param.flags.has_rest == FALSE &&
+ iseq->body->param.flags.has_post == FALSE &&
+ iseq->body->param.flags.has_kw == FALSE &&
+ iseq->body->param.flags.has_kwrest == FALSE &&
+ iseq->body->param.flags.has_block == FALSE;
+}
+
static inline void
CALLER_SETUP_ARG(struct rb_control_frame_struct *restrict cfp,
struct rb_calling_info *restrict calling,
@@ -1716,27 +1727,93 @@ CALLER_SETUP_ARG(struct rb_control_frame_struct *restrict cfp,
}
}
+#define USE_OPT_HIST 0
+
+#if USE_OPT_HIST
+#define OPT_HIST_MAX 64
+static int opt_hist[OPT_HIST_MAX+1];
+
+__attribute__((destructor))
+static void
+opt_hist_show_results_at_exit(void)
+{
+ for (int i=0; i<OPT_HIST_MAX; i++) {
+ fprintf(stderr, "opt_hist\t%d\t%d\n", i, opt_hist[i]);
+ }
+}
+#endif
+
+static VALUE
+vm_call_iseq_setup_normal_opt_start(rb_execution_context_t *ec, 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);
+ const int lead_num = iseq->body->param.lead_num;
+ const int opt = calling->argc - lead_num;
+ const int opt_num = iseq->body->param.opt_num;
+ const int opt_pc = iseq->body->param.opt_table[opt];
+ const int param = iseq->body->param.size;
+ const int local = iseq->body->local_table_size;
+ const int delta = opt_num - opt;
+
+#if USE_OPT_HIST
+ if (opt_pc < OPT_HIST_MAX) {
+ opt_hist[opt]++;
+ }
+ else {
+ opt_hist[OPT_HIST_MAX]++;
+ }
+#endif
+
+ return vm_call_iseq_setup_normal(ec, cfp, calling, cc->me, opt_pc, param - delta, local);
+}
+
static inline int
vm_callee_setup_arg(rb_execution_context_t *ec, 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(rb_simple_iseq_p(iseq) && !(ci->flag & VM_CALL_KW_SPLAT))) {
- rb_control_frame_t *cfp = ec->cfp;
+ if (LIKELY(!(ci->flag & VM_CALL_KW_SPLAT))) {
+ if (LIKELY(rb_simple_iseq_p(iseq))) {
+ rb_control_frame_t *cfp = ec->cfp;
+ CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
- CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
+ if (calling->argc != iseq->body->param.lead_num) {
+ argument_arity_error(ec, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
+ }
- if (calling->argc != iseq->body->param.lead_num) {
- argument_arity_error(ec, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
- }
+ CC_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;
+ }
+ else if (rb_iseq_only_optparam_p(iseq) && !(ci->flag & VM_CALL_KW_SPLAT)) {
+ rb_control_frame_t *cfp = ec->cfp;
+ CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
- CC_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;
- }
- else {
- return setup_parameters_complex(ec, iseq, calling, ci, argv, arg_setup_method);
+ const int lead_num = iseq->body->param.lead_num;
+ const int opt_num = iseq->body->param.opt_num;
+ const int argc = calling->argc;
+ const int opt = argc - lead_num;
+
+ if (opt < 0 || opt > opt_num) {
+ argument_arity_error(ec, iseq, argc, lead_num, lead_num + opt_num);
+ }
+
+ CC_SET_FASTPATH(cc, vm_call_iseq_setup_normal_opt_start,
+ !IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
+ !(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED));
+
+ /* initialize opt vars for self-references */
+ VM_ASSERT(iseq->body->param.size == lead_num + opt_num);
+ for (int i=argc; i<lead_num + opt_num; i++) {
+ argv[i] = Qnil;
+ }
+ return (int)iseq->body->param.opt_table[opt];
+ }
}
+
+ return setup_parameters_complex(ec, iseq, calling, ci, argv, arg_setup_method);
}
static VALUE