From 9eba45a72a63674e35d0e9dec6ddf73088ed39e8 Mon Sep 17 00:00:00 2001 From: ko1 Date: Tue, 23 Oct 2012 04:22:31 +0000 Subject: * vm_core.h, vm_insnhelper.c, vm_eval.c (OPT_CALL_CFUNC_WITHOUT_FRAME): add a new otpimization and its macro `OPT_CALL_CFUNC_WITHOUT_FRAME'. This optimization makes all cfunc method calls `frameless', which is fster than ordinal cfunc method call. If `frame' is needed (for example, it calls another method with `rb_funcall()'), then build a frame. In other words, this optimization delays frame building. However, to delay the frame building, we need additional overheads: (1) Store the last call information. (2) Check the delayed frame buidling before the frame is needed. (3) Overhead to build a delayed frame. rb_thread_t::passed_ci is storage of delayed cfunc call information. (1) is lightweight because it is only 1 assignment to `passed_ci'. To achieve (2), we modify GET_THREAD() to check `passed_ci' every time. It causes 10% overhead on my envrionment. This optimization only works for cfunc methods which do not need their `frame'. After evaluation on my environment, this optimization does not effective every time. Because of this evaluation results, this optimization is disabled at default. * vm_insnhelper.c, vm.c: add VM_PROFILE* macros to measure behaviour of VM internals. I will extend this feature. * vm_method.c, method.h: change parameters of the `invoker' function. Receive `func' pointer as the first parameter. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37293 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- vm_eval.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 80 insertions(+), 28 deletions(-) (limited to 'vm_eval.c') diff --git a/vm_eval.c b/vm_eval.c index 0cf82b2020..f4208dee38 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -49,6 +49,84 @@ vm_call0(rb_thread_t* th, VALUE recv, VALUE id, int argc, const VALUE *argv, return vm_call0_body(th, ci, argv); } +#if OPT_CALL_CFUNC_WITHOUT_FRAME +static VALUE +vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) +{ + VALUE val; + + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class); + { + rb_control_frame_t *reg_cfp = th->cfp; + const rb_method_entry_t *me = ci->me; + const rb_method_cfunc_t *cfunc = &me->def->body.cfunc; + int len = cfunc->argc; + + if (len >= 0) rb_check_arity(ci->argc, len, len); + + th->passed_ci = ci; + ci->aux.inc_sp = 0; + VM_PROFILE_UP(2); + val = (*cfunc->invoker)(cfunc->func, ci, argv); + + if (reg_cfp == th->cfp) { + if (UNLIKELY(th->passed_ci != ci)) { + rb_bug("vm_call0_cfunc: passed_ci error (ci: %p, passed_ci: %p)", ci, th->passed_ci); + } + th->passed_ci = 0; + } + else { + if (reg_cfp != th->cfp + 1) { + rb_bug("vm_call0_cfunc: cfp consistency error"); + } + VM_PROFILE_UP(3); + vm_pop_frame(th); + } + } + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class); + + return val; +} +#else +static VALUE +vm_call0_cfunc_with_frame(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) +{ + VALUE val; + + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class); + { + rb_control_frame_t *reg_cfp = th->cfp; + const rb_method_entry_t *me = ci->me; + const rb_method_cfunc_t *cfunc = &me->def->body.cfunc; + int len = cfunc->argc; + + vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, + ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), + 0, reg_cfp->sp, 1, ci->me); + + if (len >= 0) rb_check_arity(ci->argc, len, len); + + VM_PROFILE_UP(2); + val = (*cfunc->invoker)(cfunc->func, ci, argv); + + if (UNLIKELY(reg_cfp != th->cfp + 1)) { + rb_bug("vm_call0_cfunc_with_frame: cfp consistency error"); + } + VM_PROFILE_UP(3); + vm_pop_frame(th); + } + EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class); + + return val; +} + +static VALUE +vm_call0_cfunc(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) +{ + return vm_call0_cfunc_with_frame(th, ci, argv); +} +#endif + /* `ci' should point temporal value (on stack value) */ static VALUE vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) @@ -84,35 +162,9 @@ vm_call0_body(rb_thread_t* th, rb_call_info_t *ci, const VALUE *argv) break; } case VM_METHOD_TYPE_NOTIMPLEMENTED: - case VM_METHOD_TYPE_CFUNC: { - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_CALL, ci->recv, ci->mid, ci->defined_class); - { - rb_control_frame_t *reg_cfp = th->cfp; - rb_control_frame_t *cfp = vm_push_frame(th, 0, VM_FRAME_MAGIC_CFUNC, - ci->recv, ci->defined_class, VM_ENVVAL_BLOCK_PTR(ci->blockptr), - 0, reg_cfp->sp, 1, ci->me); - - cfp->me = ci->me; - - { - const rb_method_entry_t *me = ci->me; - const rb_method_definition_t *def = me->def; - int len = def->body.cfunc.argc; - - if (len >= 0) rb_check_arity(ci->argc, len, len); - - ci->aux.func = def->body.cfunc.func; - val = (*def->body.cfunc.invoker)(ci, argv); - } - - if (reg_cfp != th->cfp + 1) { - rb_bug("cfp consistency error - call0"); - } - vm_pop_frame(th); - } - EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, ci->recv, ci->mid, ci->defined_class); + case VM_METHOD_TYPE_CFUNC: + val = vm_call0_cfunc(th, ci, argv); break; - } case VM_METHOD_TYPE_ATTRSET: { rb_check_arity(ci->argc, 1, 1); val = rb_ivar_set(ci->recv, ci->me->def->body.attr.id, argv[0]); -- cgit v1.2.3