diff options
author | Koichi Sasada <ko1@atdot.net> | 2019-11-29 03:02:44 +0900 |
---|---|---|
committer | Koichi Sasada <ko1@atdot.net> | 2019-11-29 03:11:04 +0900 |
commit | dd723771c118da71aa58bb74537cacaec425542a (patch) | |
tree | 435dd65e4d2e4d6a329c511ab1640cf165dff394 /vm_insnhelper.c | |
parent | b5fbefbf2c14742f6d46ecdf3ce712062dfb1d0a (diff) |
fastpath for ivar read of FL_EXIVAR objects.
vm_getivar() provides fastpath for T_OBJECT by caching an index
of ivar. This patch also provides fastpath for FL_EXIVAR objects.
FL_EXIVAR objects have an each ivar array and index can be cached
as T_OBJECT. To access this ivar array, generic_iv_tbl is exposed
by rb_ivar_generic_ivtbl() (declared in variable.h which is newly
introduced).
Benchmark script:
Benchmark.driver(repeat_count: 3){|x|
x.executable name: 'clean', command: %w'../clean/miniruby'
x.executable name: 'trunk', command: %w'./miniruby'
objs = [Object.new, 'str', {a: 1, b: 2}, [1, 2]]
objs.each.with_index{|obj, i|
rep = obj.inspect
rep = 'Object.new' if /\#/ =~ rep
x.prelude str = %Q{
v#{i} = #{rep}
def v#{i}.foo
@iv # ivar access method (attr_reader)
end
v#{i}.instance_variable_set(:@iv, :iv)
}
puts str
x.report %Q{
v#{i}.foo
}
}
}
Result:
v0.foo # T_OBJECT
clean: 85387141.8 i/s
trunk: 85249373.6 i/s - 1.00x slower
v1.foo # T_STRING
trunk: 57894407.5 i/s
clean: 39957178.6 i/s - 1.45x slower
v2.foo # T_HASH
trunk: 56629413.2 i/s
clean: 39227088.9 i/s - 1.44x slower
v3.foo # T_ARRAY
trunk: 55797530.2 i/s
clean: 38263572.9 i/s - 1.46x slower
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r-- | vm_insnhelper.c | 106 |
1 files changed, 76 insertions, 30 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c index a99282891b..94fc7c72bd 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -18,6 +18,7 @@ #include "internal.h" #include "ruby/config.h" #include "debug_counter.h" +#include "variable.h" extern rb_method_definition_t *rb_method_definition_create(rb_method_type_t type, ID mid); extern void rb_method_definition_set(const rb_method_entry_t *me, rb_method_definition_t *def, void *opts); @@ -1011,26 +1012,48 @@ static inline VALUE vm_getivar(VALUE obj, ID id, IC ic, struct rb_call_cache *cc, int is_attr) { #if OPT_IC_FOR_IVAR - if (LIKELY(RB_TYPE_P(obj, T_OBJECT))) { - VALUE val = Qundef; - if (LIKELY(is_attr ? - RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_unset, cc->aux.index > 0) : - RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_serial, - ic->ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass)))) { - st_index_t index = !is_attr ? ic->ic_value.index : (cc->aux.index - 1); - if (LIKELY(index < ROBJECT_NUMIV(obj))) { - val = ROBJECT_IVPTR(obj)[index]; - } - } - else { - st_data_t index; - struct st_table *iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); + VALUE val = Qundef; + + if (SPECIAL_CONST_P(obj)) { + // frozen? + } + else if (LIKELY(is_attr ? + RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_unset, cc->aux.index > 0) : + RB_DEBUG_COUNTER_INC_UNLESS(ivar_get_ic_miss_serial, + ic->ic_serial == RCLASS_SERIAL(RBASIC(obj)->klass)))) { + st_index_t index = !is_attr ? ic->ic_value.index : (cc->aux.index - 1); + + RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); + + if (LIKELY(BUILTIN_TYPE(obj) == T_OBJECT) && + LIKELY(index < ROBJECT_NUMIV(obj))) { + val = ROBJECT_IVPTR(obj)[index]; + } + else if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + if (LIKELY(st_lookup(rb_ivar_generic_ivtbl(), (st_data_t)obj, (st_data_t *)&ivtbl)) && + LIKELY(index < ivtbl->numiv)) { + val = ivtbl->ivptr[index]; + } + } + goto ret; + } + else { + struct st_table *iv_index_tbl; + st_index_t numiv; + VALUE *ivptr; + + st_data_t index; + + if (BUILTIN_TYPE(obj) == T_OBJECT) { + iv_index_tbl = ROBJECT_IV_INDEX_TBL(obj); + numiv = ROBJECT_NUMIV(obj); + ivptr = ROBJECT_IVPTR(obj); + + fill: if (iv_index_tbl) { if (st_lookup(iv_index_tbl, id, &index)) { - if (index < ROBJECT_NUMIV(obj)) { - val = ROBJECT_IVPTR(obj)[index]; - } if (!is_attr) { ic->ic_value.index = index; ic->ic_serial = RCLASS_SERIAL(RBASIC(obj)->klass); @@ -1038,26 +1061,49 @@ vm_getivar(VALUE obj, ID id, IC ic, struct rb_call_cache *cc, int is_attr) else { /* call_info */ cc->aux.index = (int)index + 1; } + + if (index < numiv) { + val = ivptr[index]; + } } } } - if (UNLIKELY(val == Qundef)) { - if (!is_attr && RTEST(ruby_verbose)) - rb_warning("instance variable %"PRIsVALUE" not initialized", QUOTE_ID(id)); - val = Qnil; - } - RB_DEBUG_COUNTER_INC(ivar_get_ic_hit); - return val; - } - else { - RB_DEBUG_COUNTER_INC(ivar_get_ic_miss_noobject); + else if (FL_TEST_RAW(obj, FL_EXIVAR)) { + struct gen_ivtbl *ivtbl; + + if (LIKELY(st_lookup(rb_ivar_generic_ivtbl(), (st_data_t)obj, (st_data_t *)&ivtbl))) { + numiv = ivtbl->numiv; + ivptr = ivtbl->ivptr; + iv_index_tbl = RCLASS_IV_INDEX_TBL(rb_obj_class(obj)); + goto fill; + } + } + else { + // T_CLASS / T_MODULE + goto general_path; + } + + ret: + if (LIKELY(val != Qundef)) { + return val; + } + else { + if (!is_attr && RTEST(ruby_verbose)) { + rb_warning("instance variable %"PRIsVALUE" not initialized", QUOTE_ID(id)); + } + return Qnil; + } } + general_path: #endif /* OPT_IC_FOR_IVAR */ RB_DEBUG_COUNTER_INC(ivar_get_ic_miss); - if (is_attr) - return rb_attr_get(obj, id); - return rb_ivar_get(obj, id); + if (is_attr) { + return rb_attr_get(obj, id); + } + else { + return rb_ivar_get(obj, id); + } } static inline VALUE |