ruby-changes:65249
From: Koichi <ko1@a...>
Date: Sat, 13 Feb 2021 11:51:54 +0900 (JST)
Subject: [ruby-changes:65249] 813fe4c256 (master): opt_equality_by_mid for rb_equal_opt
https://git.ruby-lang.org/ruby.git/commit/?id=813fe4c256 From 813fe4c256f89babebb8ab53821ae5eb6bb138c6 Mon Sep 17 00:00:00 2001 From: Koichi Sasada <ko1@a...> Date: Sat, 13 Feb 2021 05:37:46 +0900 Subject: opt_equality_by_mid for rb_equal_opt This patch improves the performance of sequential and parallel execution of rb_equal() (and rb_eql()). [Bug #17497] rb_equal_opt (and rb_eql_opt) does not have own cd and it waste a time to initialize cd. This patch introduces opt_equality_by_mid() to check equality without cd. Furthermore, current master uses "static" cd on rb_equal_opt (and rb_eql_opt) and it hurts CPU caches on multi-thread execution. Now they are gone so there are no bottleneck on parallel execution. --- vm_insnhelper.c | 99 ++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 36 deletions(-) diff --git a/vm_insnhelper.c b/vm_insnhelper.c index a0e6a52..4d9e780 100644 --- a/vm_insnhelper.c +++ b/vm_insnhelper.c @@ -1854,6 +1854,7 @@ check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)()) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1854 static inline int vm_method_cfunc_is(const rb_iseq_t *iseq, CALL_DATA cd, VALUE recv, VALUE (*func)()) { + VM_ASSERT(iseq != NULL); const struct rb_callcache *cc = vm_search_method((VALUE)iseq, cd, recv); return check_cfunc(vm_cc_cme(cc), func); } @@ -1890,7 +1891,7 @@ FLONUM_2_P(VALUE a, VALUE b) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1891 } static VALUE -opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd) +opt_equality_specialized(VALUE recv, VALUE obj) { if (FIXNUM_2_P(recv, obj) && EQ_UNREDEFINED_P(INTEGER)) { goto compare_by_identity; @@ -1902,7 +1903,7 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1903 goto compare_by_identity; } else if (SPECIAL_CONST_P(recv)) { - goto compare_by_funcall; + // } else if (RBASIC_CLASS(recv) == rb_cFloat && RB_FLOAT_TYPE_P(obj) && EQ_UNREDEFINED_P(FLOAT)) { double a = RFLOAT_VALUE(recv); @@ -1932,11 +1933,7 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1933 return rb_str_eql_internal(obj, recv); } } - - compare_by_funcall: - if (! vm_method_cfunc_is(cd_owner, cd, recv, rb_obj_equal)) { - return Qundef; - } + return Qundef; compare_by_identity: if (recv == obj) { @@ -1947,47 +1944,77 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd) https://github.com/ruby/ruby/blob/trunk/vm_insnhelper.c#L1944 } } +static VALUE +opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd) +{ + VM_ASSERT(cd_owner != NULL); + + VALUE val = opt_equality_specialized(recv, obj); + if (val != Qundef) return val; + + if (!vm_method_cfunc_is(cd_owner, cd, recv, rb_obj_equal)) { + return Qundef; + } + else { + if (recv == obj) { + return Qtrue; + } + else { + return Qfalse; + } + } +} + #undef EQ_UNREDEFINED_P #ifndef MJIT_HEADER -VALUE -rb_equal_opt(VALUE obj1, VALUE obj2) + +static inline const struct rb_callcache *gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc); // vm_eval.c +NOINLINE(static VALUE opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid)); + +static VALUE +opt_equality_by_mid_slowpath(VALUE recv, VALUE obj, ID mid) { - STATIC_ASSERT(idEq_is_embeddable, VM_CI_EMBEDDABLE_P(idEq, 0, 1, 0)); + const struct rb_callcache *cc = gccct_method_search(GET_EC(), recv, mid, 1); -#if USE_EMBED_CI - static struct rb_call_data cd = { - .ci = vm_ci_new_id(idEq, 0, 1, 0), - }; -#else - struct rb_call_data cd = { - .ci = &VM_CI_ON_STACK(idEq, 0, 1, 0), - }; -#endif + if (cc && check_cfunc(vm_cc_cme(cc), rb_obj_equal)) { + if (recv == obj) { + return Qtrue; + } + else { + return Qfalse; + } + } + else { + return Qundef; + } +} - cd.cc = &vm_empty_cc; - return opt_equality(NULL, obj1, obj2, &cd); +static VALUE +opt_equality_by_mid(VALUE recv, VALUE obj, ID mid) +{ + VALUE val = opt_equality_specialized(recv, obj); + if (val != Qundef) { + return val; + } + else { + return opt_equality_by_mid_slowpath(recv, obj, mid); + } } VALUE -rb_eql_opt(VALUE obj1, VALUE obj2) +rb_equal_opt(VALUE obj1, VALUE obj2) { - STATIC_ASSERT(idEqlP_is_embeddable, VM_CI_EMBEDDABLE_P(idEqlP, 0, 1, 0)); - -#if USE_EMBED_CI - static struct rb_call_data cd = { - .ci = vm_ci_new_id(idEqlP, 0, 1, 0), - }; -#else - struct rb_call_data cd = { - .ci = &VM_CI_ON_STACK(idEqlP, 0, 1, 0), - }; -#endif + return opt_equality_by_mid(obj1, obj2, idEq); +} - cd.cc = &vm_empty_cc; - return opt_equality(NULL, obj1, obj2, &cd); +VALUE +rb_eql_opt(VALUE obj1, VALUE obj2) +{ + return opt_equality_by_mid(obj1, obj2, idEqlP); } -#endif + +#endif // MJIT_HEADER extern VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE, ID, int, const VALUE*, const rb_callable_method_entry_t *, int kw_splat); -- cgit v1.1 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/