Skip to content

Commit

Permalink
reuse cc->call
Browse files Browse the repository at this point in the history
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
  • Loading branch information
shyouhei committed Sep 19, 2019
1 parent 9fb9f2d commit d74fa8e
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 7 deletions.
25 changes: 25 additions & 0 deletions benchmark/vm2_poly_same_method.yml
@@ -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
2 changes: 1 addition & 1 deletion insns.def
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions internal.h
Expand Up @@ -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;
Expand All @@ -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,
Expand Down
37 changes: 31 additions & 6 deletions vm_insnhelper.c
Expand Up @@ -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
Expand Down

0 comments on commit d74fa8e

Please sign in to comment.