1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

reuse cc->call

I noticed that in case of cache misshit, re-calculated cc->me can
be the same method entry than the pevious one.  That is an okay
situation but can't we partially reuse the cache, because cc->call
should still be valid then?

One thing that has to be special-cased is when the method entry
gets amended by some refinements.  That happens behind-the-scene
of call cache mechanism.  We have to check if cc->me->def points to
the previously saved one.

Calculating -------------------------------------
                          trunk        ours
vm2_poly_same_method     1.534M      2.025M i/s -      6.000M times in 3.910203s 2.962752s

Comparison:
             vm2_poly_same_method
                ours:   2025143.9 i/s
               trunk:   1534447.2 i/s - 1.32x  slower
This commit is contained in:
卜部昌平 2019-09-18 17:18:48 +09:00
parent 9fb9f2d318
commit d74fa8e55c
Notes: git 2019-09-19 15:18:37 +09:00
4 changed files with 59 additions and 7 deletions

View file

@ -0,0 +1,25 @@
prelude: |
module AR; end
class AR::Base
def create_or_update
nil
end
def save
create_or_update
end
end
class Foo < AR::Base; end
class Bar < AR::Base; end
o1 = Foo.new
o2 = Bar.new
benchmark:
vm2_poly_same_method: |
o1.save; o2.save;
o1.save; o2.save;
o1.save; o2.save;
o1.save; o2.save;
o1.save; o2.save;
o1.save; o2.save;
o1.save; o2.save;
o1.save; o2.save;
loop_count: 6000000

View file

@ -911,7 +911,7 @@ invokeblock
// attr rb_snum_t sp_inc = sp_inc_of_invokeblock(ci);
{
static struct rb_call_cache cc = {
0, 0, NULL, vm_invokeblock_i,
0, 0, NULL, NULL, vm_invokeblock_i,
};
VALUE bh = VM_BLOCK_HANDLER_NONE;

View file

@ -2328,6 +2328,7 @@ enum method_missing_reason {
MISSING_NONE = 0x40
};
struct rb_callable_method_entry_struct;
struct rb_method_definition_struct;
struct rb_execution_context_struct;
struct rb_control_frame_struct;
struct rb_calling_info;
@ -2339,6 +2340,7 @@ struct rb_call_cache {
/* inline cache: values */
const struct rb_callable_method_entry_struct *me;
const struct rb_method_definition_struct *def;
VALUE (*call)(struct rb_execution_context_struct *ec,
struct rb_control_frame_struct *cfp,

View file

@ -1374,16 +1374,41 @@ vm_expandarray(VALUE *sp, VALUE ary, rb_num_t num, int flag)
static VALUE vm_call_general(rb_execution_context_t *ec, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci, struct rb_call_cache *cc);
#ifdef __has_attribute
#if __has_attribute(artificial)
__attribute__((__artificial__))
#endif
#endif
static inline vm_call_handler
calccall(const struct rb_call_cache *cc, const rb_callable_method_entry_t *me)
{
if (UNLIKELY(!me)) {
return vm_call_general; /* vm_call_method_nome() situation */
}
else if (LIKELY(cc->me != me)) {
return vm_call_general; /* normal cases */
}
else if (UNLIKELY(cc->def != me->def)) {
return vm_call_general; /* cc->me was refined elsewhere */
}
else {
return cc->call;
}
}
MJIT_FUNC_EXPORTED void
rb_vm_search_method_slowpath(const struct rb_call_info *ci, struct rb_call_cache *cc, VALUE klass)
{
cc->me = rb_callable_method_entry(klass, ci->mid);
const rb_callable_method_entry_t *me =
rb_callable_method_entry(klass, ci->mid);
*cc = (struct rb_call_cache) {
GET_GLOBAL_METHOD_STATE(),
RCLASS_SERIAL(klass),
me,
me ? me->def : NULL,
calccall(cc, me),
};
VM_ASSERT(callable_method_entry_p(cc->me));
cc->call = vm_call_general;
#if OPT_INLINE_METHOD_CACHE
cc->method_state = GET_GLOBAL_METHOD_STATE();
cc->class_serial = RCLASS_SERIAL(klass);
#endif
}
static void