mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
* vm_insnhelper.c: introduce new call handler for simple ISeqs.
vm_call_iseq_setup_normal_0start() is simple, however it has some loops/conditions depends on ISeq::param.size and ISeq::local_size (in vm_push_frame(), inlined into this function). There are many simple methods which has a few parameters and local variables. So that this patch introduces several special functions generated in vm_call_iseq_optimized.inc by tool/mk_call_iseq_optimized.rb. This script makes vm_call_iseq_setup_normal_0start_Xparams_Ylocals() where X is 0 to 3 and Y is 1 to 6 (as current setting). In this case, X * Y = 24 functions are created. These functions creates fast method dispatch by inlining vm_push_frame() with immediate params/locals sizes. On my laptop, we can have the following results. vm2_method* 1.083 (8.3% faster) vm2_poly_method* 0.961 (3.4% slower) It shows 8.3% faster for inner loop method dispatch (hit inline cache), but 3.4% slower when inline cache miss because we need to find a suitable call handler. * common.mk: add a rule for vm_call_iseq_optimized.inc. * tool/mk_call_iseq_optimized.rb: added. * vm.c: include vm_call_iseq_optimized.inc. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52254 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
1e0600324b
commit
f9697c5637
5 changed files with 175 additions and 49 deletions
36
ChangeLog
36
ChangeLog
|
@ -1,3 +1,39 @@
|
|||
Sat Oct 24 02:02:24 2015 Koichi Sasada <ko1@atdot.net>
|
||||
|
||||
* vm_insnhelper.c: introduce new call handler for simple ISeqs.
|
||||
|
||||
vm_call_iseq_setup_normal_0start() is simple, however it has
|
||||
some loops/conditions depends on ISeq::param.size and
|
||||
ISeq::local_size (in vm_push_frame(), inlined into this function).
|
||||
|
||||
There are many simple methods which has a few parameters and local
|
||||
variables. So that this patch introduces several special functions
|
||||
generated in vm_call_iseq_optimized.inc by
|
||||
tool/mk_call_iseq_optimized.rb.
|
||||
|
||||
This script makes
|
||||
vm_call_iseq_setup_normal_0start_Xparams_Ylocals()
|
||||
where X is 0 to 3 and Y is 1 to 6 (as current setting).
|
||||
In this case, X * Y = 24 functions are created.
|
||||
|
||||
These functions creates fast method dispatch by inlining
|
||||
vm_push_frame() with immediate params/locals sizes.
|
||||
|
||||
On my laptop, we can have the following results.
|
||||
|
||||
vm2_method* 1.083 (8.3% faster)
|
||||
vm2_poly_method* 0.961 (3.4% slower)
|
||||
|
||||
It shows 8.3% faster for inner loop method dispatch (hit inline
|
||||
cache), but 3.4% slower when inline cache miss because we need
|
||||
to find a suitable call handler.
|
||||
|
||||
* common.mk: add a rule for vm_call_iseq_optimized.inc.
|
||||
|
||||
* tool/mk_call_iseq_optimized.rb: added.
|
||||
|
||||
* vm.c: include vm_call_iseq_optimized.inc.
|
||||
|
||||
Sat Oct 24 01:58:50 2015 Koichi Sasada <ko1@atdot.net>
|
||||
|
||||
* vm_core.h: define vm_call_handler.
|
||||
|
|
|
@ -819,6 +819,10 @@ known_errors.inc: $(srcdir)/template/known_errors.inc.tmpl $(srcdir)/defs/known_
|
|||
$(ECHO) generating $@
|
||||
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb -c -o $@ $(srcdir)/template/known_errors.inc.tmpl $(srcdir)/defs/known_errors.def
|
||||
|
||||
vm_call_iseq_optimized.inc: $(srcdir)/tool/mk_call_iseq_optimized.rb
|
||||
$(ECHO) generating $@
|
||||
$(Q) $(BASERUBY) $(srcdir)/tool/mk_call_iseq_optimized.rb > vm_call_iseq_optimized.inc
|
||||
|
||||
$(MINIPRELUDE_C): $(COMPILE_PRELUDE)
|
||||
$(ECHO) generating $@
|
||||
$(Q) $(BASERUBY) $(srcdir)/tool/generic_erb.rb -I$(srcdir) -o $@ \
|
||||
|
@ -2391,6 +2395,7 @@ vm.$(OBJEXT): {$(VPATH)}thread_native.h
|
|||
vm.$(OBJEXT): {$(VPATH)}vm.c
|
||||
vm.$(OBJEXT): {$(VPATH)}vm.h
|
||||
vm.$(OBJEXT): {$(VPATH)}vm.inc
|
||||
vm.$(OBJEXT): {$(VPATH)}vm_call_iseq_optimized.inc
|
||||
vm.$(OBJEXT): {$(VPATH)}vm_args.c
|
||||
vm.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||
vm.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||
|
@ -2431,6 +2436,9 @@ vm_backtrace.$(OBJEXT): {$(VPATH)}vm_backtrace.c
|
|||
vm_backtrace.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||
vm_backtrace.$(OBJEXT): {$(VPATH)}vm_debug.h
|
||||
vm_backtrace.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
vm_call.$(OBJEXT): $(top_srcdir)/include/ruby.h
|
||||
vm_call.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||
vm_call.$(OBJEXT): {$(VPATH)}vm_call_iseq_optimized.inc
|
||||
vm_dump.$(OBJEXT): $(CCAN_DIR)/check_type/check_type.h
|
||||
vm_dump.$(OBJEXT): $(CCAN_DIR)/container_of/container_of.h
|
||||
vm_dump.$(OBJEXT): $(CCAN_DIR)/list/list.h
|
||||
|
|
72
tool/mk_call_iseq_optimized.rb
Normal file
72
tool/mk_call_iseq_optimized.rb
Normal file
|
@ -0,0 +1,72 @@
|
|||
|
||||
puts <<EOS
|
||||
#if 1 /* enable or disable this optimization */
|
||||
|
||||
/* DO NOT EDIT THIS FILE DIRECTLY
|
||||
*
|
||||
* This file is enerated by tool/mkcall_iseq.rb
|
||||
*/
|
||||
|
||||
EOS
|
||||
|
||||
P = (0..3)
|
||||
L = (1..6)
|
||||
|
||||
def fname param, local
|
||||
"vm_call_iseq_setup_normal_0start_#{param}params_#{local}locals"
|
||||
end
|
||||
|
||||
P.each{|param|
|
||||
L.each{|local|
|
||||
puts <<EOS
|
||||
static VALUE
|
||||
#{fname(param, local)}(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
|
||||
{
|
||||
return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0, #{param}, #{local});
|
||||
}
|
||||
|
||||
EOS
|
||||
#
|
||||
}
|
||||
}
|
||||
|
||||
puts <<EOS
|
||||
/* vm_call_iseq_handlers[param][local] */
|
||||
static const vm_call_handler vm_call_iseq_handlers[][#{L.to_a.size}] = {
|
||||
#{P.map{|param| '{' + L.map{|local| fname(param, local)}.join(",\n ") + '}'}.join(",\n")}
|
||||
};
|
||||
|
||||
static inline vm_call_handler
|
||||
vm_call_iseq_setup_func(const struct rb_call_info *ci, const int param_size, const int local_size)
|
||||
{
|
||||
if (UNLIKELY(ci->flag & VM_CALL_TAILCALL)) {
|
||||
return vm_call_iseq_setup_tailcall_0start;
|
||||
}
|
||||
else if (0) { /* to disable optimize */
|
||||
return vm_call_iseq_setup_normal_0start;
|
||||
}
|
||||
else {
|
||||
if (param_size <= #{P.end} &&
|
||||
local_size <= #{L.end}) {
|
||||
VM_ASSERT(local_size != 0);
|
||||
return vm_call_iseq_handlers[param_size][local_size-1];
|
||||
}
|
||||
return vm_call_iseq_setup_normal_0start;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
||||
static inline vm_call_handler
|
||||
vm_call_iseq_setup_func(const struct rb_call_info *ci, struct rb_call_cache *cc)
|
||||
{
|
||||
if (UNLIKELY(ci->flag & VM_CALL_TAILCALL)) {
|
||||
return vm_call_iseq_setup_tailcall_0start;
|
||||
}
|
||||
else {
|
||||
return vm_call_iseq_setup_normal_0start;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
EOS
|
2
vm.c
2
vm.c
|
@ -3111,3 +3111,5 @@ vm_collect_usage_register(int reg, int isset)
|
|||
(*ruby_vm_collect_usage_func_register)(reg, isset);
|
||||
}
|
||||
#endif
|
||||
|
||||
#include "vm_call_iseq_optimized.inc" /* required from vm_insnhelper.c */
|
||||
|
|
106
vm_insnhelper.c
106
vm_insnhelper.c
|
@ -1235,17 +1235,17 @@ vm_base_ptr(rb_control_frame_t *cfp)
|
|||
|
||||
#include "vm_args.c"
|
||||
|
||||
static inline VALUE vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
|
||||
static inline VALUE vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
|
||||
static inline VALUE vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc, int param_size, int local_size);
|
||||
static inline VALUE vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc, int param_size, int local_size);
|
||||
static inline VALUE vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc);
|
||||
static VALUE vm_call_iseq_setup_normal_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
static VALUE vm_call_iseq_setup_tailcall_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
|
||||
static VALUE vm_call_super_method(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
static VALUE vm_call_method_nome(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
static VALUE vm_call_method_each_type(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
static inline VALUE vm_call_method(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
|
||||
typedef VALUE (*vm_call_handler)(struct rb_thread_struct *th, struct rb_control_frame_struct *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
|
||||
static vm_call_handler vm_call_iseq_setup_func(const struct rb_call_info *ci, const int param_size, const int local_size);
|
||||
|
||||
static rb_method_definition_t *method_definition_create(rb_method_type_t type, ID mid);
|
||||
static void method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts);
|
||||
static int rb_method_definition_eq(const rb_method_definition_t *d1, const rb_method_definition_t *d2);
|
||||
|
@ -1332,30 +1332,6 @@ vm_callee_setup_block_arg(rb_thread_t *th, struct rb_calling_info *calling, cons
|
|||
}
|
||||
}
|
||||
|
||||
static inline int
|
||||
vm_callee_setup_arg(rb_thread_t *th, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, const rb_iseq_t *iseq, VALUE *argv)
|
||||
{
|
||||
if (LIKELY(simple_iseq_p(iseq))) {
|
||||
rb_control_frame_t *cfp = th->cfp;
|
||||
|
||||
CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
|
||||
|
||||
if (calling->argc != iseq->body->param.lead_num) {
|
||||
argument_arity_error(th, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
|
||||
}
|
||||
|
||||
CI_SET_FASTPATH(cc,
|
||||
(UNLIKELY(ci->flag & VM_CALL_TAILCALL) ? vm_call_iseq_setup_tailcall_0start :
|
||||
vm_call_iseq_setup_normal_0start),
|
||||
(!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
|
||||
!(METHOD_ENTRY_VISI(cc->me) == METHOD_VISI_PROTECTED)));
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
return setup_parameters_complex(th, iseq, calling, ci, argv, arg_setup_method);
|
||||
}
|
||||
}
|
||||
|
||||
static const rb_iseq_t *
|
||||
def_iseq_ptr(rb_method_definition_t *def)
|
||||
{
|
||||
|
@ -1365,18 +1341,60 @@ def_iseq_ptr(rb_method_definition_t *def)
|
|||
return def->body.iseq.iseqptr;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_call_iseq_setup_tailcall_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
|
||||
{
|
||||
return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, 0);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_call_iseq_setup_normal_0start(rb_thread_t *th, 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);
|
||||
int param = iseq->body->param.size;
|
||||
int local = iseq->body->local_size;
|
||||
return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0, param, local);
|
||||
}
|
||||
|
||||
static inline int
|
||||
vm_callee_setup_arg(rb_thread_t *th, 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(simple_iseq_p(iseq))) {
|
||||
rb_control_frame_t *cfp = th->cfp;
|
||||
|
||||
CALLER_SETUP_ARG(cfp, calling, ci); /* splat arg */
|
||||
|
||||
if (calling->argc != iseq->body->param.lead_num) {
|
||||
argument_arity_error(th, iseq, calling->argc, iseq->body->param.lead_num, iseq->body->param.lead_num);
|
||||
}
|
||||
|
||||
CI_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(th, iseq, calling, ci, argv, arg_setup_method);
|
||||
}
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_call_iseq_setup(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
|
||||
{
|
||||
int opt_pc = vm_callee_setup_arg(th, calling, ci, cc, def_iseq_ptr(cc->me->def), cfp->sp - calling->argc);
|
||||
return vm_call_iseq_setup_2(th, cfp, calling, ci, cc, opt_pc);
|
||||
const rb_iseq_t *iseq = def_iseq_ptr(cc->me->def);
|
||||
const int param_size = iseq->body->param.size;
|
||||
const int local_size = iseq->body->local_size;
|
||||
const int opt_pc = vm_callee_setup_arg(th, calling, ci, cc, def_iseq_ptr(cc->me->def), cfp->sp - calling->argc, param_size, local_size);
|
||||
return vm_call_iseq_setup_2(th, cfp, calling, ci, cc, opt_pc, param_size, local_size);
|
||||
}
|
||||
|
||||
static inline VALUE
|
||||
vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc)
|
||||
vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
|
||||
int opt_pc, int param_size, int local_size)
|
||||
{
|
||||
if (LIKELY(!(ci->flag & VM_CALL_TAILCALL))) {
|
||||
return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, opt_pc);
|
||||
return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, opt_pc, param_size, local_size);
|
||||
}
|
||||
else {
|
||||
return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, opt_pc);
|
||||
|
@ -1384,24 +1402,26 @@ vm_call_iseq_setup_2(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling
|
|||
}
|
||||
|
||||
static inline VALUE
|
||||
vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc)
|
||||
vm_call_iseq_setup_normal(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
|
||||
int opt_pc, int param_size, int local_size)
|
||||
{
|
||||
const rb_callable_method_entry_t *me = cc->me;
|
||||
const rb_iseq_t *iseq = def_iseq_ptr(me->def);
|
||||
VALUE *argv = cfp->sp - calling->argc;
|
||||
VALUE *sp = argv + iseq->body->param.size;
|
||||
VALUE *sp = argv + param_size;
|
||||
cfp->sp = argv - 1 /* recv */;
|
||||
|
||||
vm_push_frame(th, iseq, VM_FRAME_MAGIC_METHOD, calling->recv,
|
||||
VM_ENVVAL_BLOCK_PTR(calling->blockptr), (VALUE)me,
|
||||
iseq->body->iseq_encoded + opt_pc, sp,
|
||||
iseq->body->local_size - iseq->body->param.size,
|
||||
local_size - param_size,
|
||||
iseq->body->stack_max);
|
||||
return Qundef;
|
||||
}
|
||||
|
||||
static inline VALUE
|
||||
vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc, int opt_pc)
|
||||
vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc,
|
||||
int opt_pc)
|
||||
{
|
||||
unsigned int i;
|
||||
VALUE *argv = cfp->sp - calling->argc;
|
||||
|
@ -1436,18 +1456,6 @@ vm_call_iseq_setup_tailcall(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_
|
|||
return Qundef;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_call_iseq_setup_normal_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
|
||||
{
|
||||
return vm_call_iseq_setup_normal(th, cfp, calling, ci, cc, 0);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
vm_call_iseq_setup_tailcall_0start(rb_thread_t *th, rb_control_frame_t *cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc)
|
||||
{
|
||||
return vm_call_iseq_setup_tailcall(th, cfp, calling, ci, cc, 0);
|
||||
}
|
||||
|
||||
static VALUE
|
||||
call_cfunc_m2(VALUE (*func)(ANYARGS), VALUE recv, int argc, const VALUE *argv)
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue