summaryrefslogtreecommitdiff
path: root/vm_insnhelper.c
diff options
context:
space:
mode:
authorNARUSE, Yui <naruse@airemix.jp>2021-03-11 20:24:48 +0900
committerNARUSE, Yui <naruse@airemix.jp>2021-03-11 20:24:48 +0900
commitde6072a22edbaab3793cf7f976cc9e0118d0df40 (patch)
tree08c22551519d9c01b02e45c888713dbf6107f855 /vm_insnhelper.c
parent0074ea2d83230e10ab0cd769fff203c13d3c592f (diff)
merge revision(s) abdc634f64a440afcdc7f23c9757d27aab4db8a9,083c5f08ec4e95c9b75810d46f933928327a5ab3,1ecda213668644d656eb0d60654737482447dd92,813fe4c256f89babebb8ab53821ae5eb6bb138c6: [Backport #17497]
remove unused decl --- internal/vm.h | 6 ------ vm_args.c | 2 -- 2 files changed, 8 deletions(-) Check stack overflow in recursive glob_helper [Bug #17162] --- dir.c | 2 ++ internal/vm.h | 1 + vm_eval.c | 10 ++++++++++ 3 files changed, 13 insertions(+) global call-cache cache table for rb_funcall* rb_funcall* (rb_funcall(), rb_funcallv(), ...) functions invokes Ruby's method with given receiver. Ruby 2.7 introduced inline method cache with static memory area. However, Ruby 3.0 reimplemented the method cache data structures and the inline cache was removed. Without inline cache, rb_funcall* searched methods everytime. Most of cases per-Class Method Cache (pCMC) will be helped but pCMC requires VM-wide locking and it hurts performance on multi-Ractor execution, especially all Ractors calls methods with rb_funcall*. This patch introduced Global Call-Cache Cache Table (gccct) for rb_funcall*. Call-Cache was introduced from Ruby 3.0 to manage method cache entry atomically and gccct enables method-caching without VM-wide locking. This table solves the performance issue on multi-ractor execution. [Bug #17497] Ruby-level method invocation does not use gccct because it has inline-method-cache and the table size is limited. Basically rb_funcall* is not used frequently, so 1023 entries can be enough. We will revisit the table size if it is not enough. --- debug_counter.h | 3 + vm.c | 12 +++ vm_callinfo.h | 12 --- vm_core.h | 5 + vm_eval.c | 288 ++++++++++++++++++++++++++++++++++++++++++-------------- vm_insnhelper.c | 11 ++- vm_method.c | 14 ++- 7 files changed, 255 insertions(+), 90 deletions(-) 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(-)
Diffstat (limited to 'vm_insnhelper.c')
-rw-r--r--vm_insnhelper.c110
1 files changed, 69 insertions, 41 deletions
diff --git a/vm_insnhelper.c b/vm_insnhelper.c
index 6bbb4bca9b..8a876851b9 100644
--- a/vm_insnhelper.c
+++ b/vm_insnhelper.c
@@ -1856,6 +1856,7 @@ check_cfunc(const rb_callable_method_entry_t *me, VALUE (*func)())
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);
}
@@ -1892,7 +1893,7 @@ FLONUM_2_P(VALUE a, VALUE b)
}
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;
@@ -1904,7 +1905,7 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
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);
@@ -1934,11 +1935,7 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
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) {
@@ -1949,47 +1946,77 @@ opt_equality(const rb_iseq_t *cd_owner, VALUE recv, VALUE obj, CALL_DATA cd)
}
}
+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);
@@ -2362,6 +2389,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
{
const struct rb_callinfo *ci = calling->ci;
const struct rb_callcache *cc = calling->cc;
+ bool cacheable_ci = vm_ci_markable(ci);
if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_KW_SPLAT))) {
if (LIKELY(rb_simple_iseq_p(iseq))) {
@@ -2375,7 +2403,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
VM_ASSERT(ci == calling->ci);
VM_ASSERT(cc == calling->cc);
- CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size), vm_call_iseq_optimizable_p(ci, cc));
+ CC_SET_FASTPATH(cc, vm_call_iseq_setup_func(ci, param_size, local_size), cacheable_ci && vm_call_iseq_optimizable_p(ci, cc));
return 0;
}
else if (rb_iseq_only_optparam_p(iseq)) {
@@ -2395,12 +2423,12 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (LIKELY(!(vm_ci_flag(ci) & VM_CALL_TAILCALL))) {
CC_SET_FASTPATH(cc, vm_call_iseq_setup_normal_opt_start,
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
}
else {
CC_SET_FASTPATH(cc, vm_call_iseq_setup_tailcall_opt_start,
!IS_ARGS_SPLAT(ci) && !IS_ARGS_KEYWORD(ci) &&
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
}
/* initialize opt vars for self-references */
@@ -2428,7 +2456,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
args_setup_kw_parameters(ec, iseq, ci_kws, ci_kw_len, ci_keywords, klocals);
CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_kwarg,
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
return 0;
}
@@ -2441,7 +2469,7 @@ vm_callee_setup_arg(rb_execution_context_t *ec, struct rb_calling_info *calling,
if (klocals[kw_param->num] == INT2FIX(0)) {
/* copy from default_values */
CC_SET_FASTPATH(cc, vm_call_iseq_setup_kwparm_nokwarg,
- METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
+ cacheable_ci && METHOD_ENTRY_CACHEABLE(vm_cc_cme(cc)));
}
return 0;