ruby-changes:62603
From: Alan <ko1@a...>
Date: Tue, 18 Aug 2020 06:18:06 +0900 (JST)
Subject: [ruby-changes:62603] 264e4cd04f (master): Remove write barrier exemption for T_ICLASS
https://git.ruby-lang.org/ruby.git/commit/?id=264e4cd04f From 264e4cd04fbcdcb739a1ff9a84e19afe66005cb2 Mon Sep 17 00:00:00 2001 From: Alan Wu <XrXr@u...> Date: Mon, 10 Aug 2020 18:19:17 -0400 Subject: Remove write barrier exemption for T_ICLASS Before this commit, iclasses were "shady", or not protected by write barriers. Because of that, the GC needs to spend more time marking these objects than otherwise. Applications that make heavy use of modules should see reduction in GC time as they have a significant number of live iclasses on the heap. - Put logic for iclass method table ownership into a function - Remove calls to WB_UNPROTECT and insert write barriers for iclasses This commit relies on the following invariant: for any non oirigin iclass `I`, `RCLASS_M_TBL(I) == RCLASS_M_TBL(RBasic(I)->klass)`. This invariant did not hold prior to 98286e9 for classes and modules that have prepended modules. [Feature #16984] diff --git a/class.c b/class.c index 0d97603..9876e99 100644 --- a/class.c +++ b/class.c @@ -911,8 +911,7 @@ rb_include_class_new(VALUE module, VALUE super) https://github.com/ruby/ruby/blob/trunk/class.c#L911 { VALUE klass = class_alloc(T_ICLASS, rb_cClass); - RCLASS_M_TBL(OBJ_WB_UNPROTECT(klass)) = - RCLASS_M_TBL(OBJ_WB_UNPROTECT(module)); /* TODO: unprotected? */ + RCLASS_M_TBL(klass) = RCLASS_M_TBL(module); RCLASS_SET_ORIGIN(klass, klass); if (BUILTIN_TYPE(module) == T_ICLASS) { @@ -1098,13 +1097,12 @@ move_refined_method(ID key, VALUE value, void *data) https://github.com/ruby/ruby/blob/trunk/class.c#L1097 const rb_method_entry_t *orig_me = me->def->body.refined.orig_me, *new_me; RB_OBJ_WRITE(me, &me->def->body.refined.orig_me, NULL); new_me = rb_method_entry_clone(me); - rb_id_table_insert(tbl, key, (VALUE)new_me); - RB_OBJ_WRITTEN(klass, Qundef, new_me); + rb_method_table_insert(klass, tbl, key, new_me); rb_method_entry_copy(me, orig_me); return ID_TABLE_CONTINUE; } else { - rb_id_table_insert(tbl, key, (VALUE)me); + rb_method_table_insert(klass, tbl, key, me); return ID_TABLE_DELETE; } } @@ -1119,7 +1117,6 @@ ensure_origin(VALUE klass) https://github.com/ruby/ruby/blob/trunk/class.c#L1117 VALUE origin = RCLASS_ORIGIN(klass); if (origin == klass) { origin = class_alloc(T_ICLASS, klass); - OBJ_WB_UNPROTECT(origin); /* TODO: conservative shading. Need more survey. */ RCLASS_SET_SUPER(origin, RCLASS_SUPER(klass)); RCLASS_SET_SUPER(klass, origin); RCLASS_SET_ORIGIN(klass, origin); diff --git a/eval.c b/eval.c index 237d9ac..e4fec3d 100644 --- a/eval.c +++ b/eval.c @@ -1420,8 +1420,7 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module) https://github.com/ruby/ruby/blob/trunk/eval.c#L1420 c = iclass = rb_include_class_new(module, superclass); RB_OBJ_WRITE(c, &RCLASS_REFINED_CLASS(c), klass); - RCLASS_M_TBL(OBJ_WB_UNPROTECT(c)) = - RCLASS_M_TBL(OBJ_WB_UNPROTECT(module)); /* TODO: check unprotecting */ + RCLASS_M_TBL(c) = RCLASS_M_TBL(module); module = RCLASS_SUPER(module); while (module && module != klass) { diff --git a/gc.c b/gc.c index 788f06f..11a79e6 100644 --- a/gc.c +++ b/gc.c @@ -2854,8 +2854,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj) https://github.com/ruby/ruby/blob/trunk/gc.c#L2854 break; case T_ICLASS: /* Basically , T_ICLASS shares table with the module */ - if (FL_TEST(obj, RICLASS_IS_ORIGIN) && - !FL_TEST(obj, RICLASS_ORIGIN_SHARED_MTBL)) { + if (RICLASS_OWNS_M_TBL_P(obj)) { /* Method table is not shared for origin iclasses of classes */ rb_id_table_free(RCLASS_M_TBL(obj)); } @@ -3974,8 +3973,7 @@ obj_memsize_of(VALUE obj, int use_all_types) https://github.com/ruby/ruby/blob/trunk/gc.c#L3973 } break; case T_ICLASS: - if (FL_TEST(obj, RICLASS_IS_ORIGIN) && - !FL_TEST(obj, RICLASS_ORIGIN_SHARED_MTBL)) { + if (RICLASS_OWNS_M_TBL_P(obj)) { if (RCLASS_M_TBL(obj)) { size += rb_id_table_memsize(RCLASS_M_TBL(obj)); } @@ -5504,8 +5502,7 @@ gc_mark_children(rb_objspace_t *objspace, VALUE obj) https://github.com/ruby/ruby/blob/trunk/gc.c#L5502 break; case T_ICLASS: - if (FL_TEST(obj, RICLASS_IS_ORIGIN) && - !FL_TEST(obj, RICLASS_ORIGIN_SHARED_MTBL)) { + if (RICLASS_OWNS_M_TBL_P(obj)) { mark_m_tbl(objspace, RCLASS_M_TBL(obj)); } if (RCLASS_SUPER(obj)) { diff --git a/internal/class.h b/internal/class.h index 4093825..663a1e8 100644 --- a/internal/class.h +++ b/internal/class.h @@ -144,6 +144,12 @@ RICLASS_SET_ORIGIN_SHARED_MTBL(VALUE iclass) https://github.com/ruby/ruby/blob/trunk/internal/class.h#L144 FL_SET(iclass, RICLASS_ORIGIN_SHARED_MTBL); } +static inline bool +RICLASS_OWNS_M_TBL_P(VALUE iclass) +{ + return FL_TEST_RAW(iclass, RICLASS_IS_ORIGIN | RICLASS_ORIGIN_SHARED_MTBL) == RICLASS_IS_ORIGIN; +} + static inline void RCLASS_SET_INCLUDER(VALUE iclass, VALUE klass) { diff --git a/method.h b/method.h index 6bc494f..dae0a4f 100644 --- a/method.h +++ b/method.h @@ -188,6 +188,8 @@ struct rb_method_definition_struct { https://github.com/ruby/ruby/blob/trunk/method.h#L188 uintptr_t method_serial; }; +struct rb_id_table; + typedef struct rb_method_definition_struct rb_method_definition_t; STATIC_ASSERT(sizeof_method_def, offsetof(rb_method_definition_t, body)==8); @@ -230,6 +232,8 @@ const rb_method_entry_t *rb_method_entry_clone(const rb_method_entry_t *me); https://github.com/ruby/ruby/blob/trunk/method.h#L232 const rb_callable_method_entry_t *rb_method_entry_complement_defined_class(const rb_method_entry_t *src_me, ID called_id, VALUE defined_class); void rb_method_entry_copy(rb_method_entry_t *dst, const rb_method_entry_t *src); +void rb_method_table_insert(VALUE klass, struct rb_id_table *table, ID method_id, const rb_method_entry_t *me); + void rb_scope_visibility_set(rb_method_visibility_t); VALUE rb_unnamed_parameters(int arity); diff --git a/vm_method.c b/vm_method.c index 037ac2a..58516b9 100644 --- a/vm_method.c +++ b/vm_method.c @@ -174,11 +174,9 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L174 // invalidate cc by invalidating cc->cme VALUE owner = cme->owner; VM_ASSERT(BUILTIN_TYPE(owner) == T_CLASS); - rb_callable_method_entry_t *new_cme = - (rb_callable_method_entry_t *)rb_method_entry_clone((const rb_method_entry_t *)cme); - struct rb_id_table *mtbl = RCLASS_M_TBL(RCLASS_ORIGIN(owner)); - rb_id_table_insert(mtbl, mid, (VALUE)new_cme); - RB_OBJ_WRITTEN(owner, cme, new_cme); + const rb_method_entry_t *new_cme = rb_method_entry_clone((const rb_method_entry_t *)cme); + VALUE origin = RCLASS_ORIGIN(owner); + rb_method_table_insert(origin, RCLASS_M_TBL(origin), mid, new_cme); } vm_me_invalidate_cache((rb_callable_method_entry_t *)cme); RB_DEBUG_COUNTER_INC(cc_invalidate_tree_cme); @@ -261,6 +259,19 @@ rb_clear_method_cache_all(void) https://github.com/ruby/ruby/blob/trunk/vm_method.c#L259 rb_objspace_each_objects(invalidate_all_cc, NULL); } +void +rb_method_table_insert(VALUE klass, struct rb_id_table *table, ID method_id, const rb_method_entry_t *me) +{ + VALUE table_owner = klass; + if (RB_TYPE_P(klass, T_ICLASS) && !RICLASS_OWNS_M_TBL_P(klass)) { + table_owner = RBASIC(table_owner)->klass; + } + VM_ASSERT(RB_TYPE_P(table_owner, T_CLASS) || RB_TYPE_P(table_owner, T_ICLASS) || RB_TYPE_P(table_owner, T_MODULE)); + VM_ASSERT(table == RCLASS_M_TBL(table_owner)); + rb_id_table_insert(table, method_id, (VALUE)me); + RB_OBJ_WRITTEN(table_owner, Qundef, (VALUE)me); +} + VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker) { @@ -802,8 +813,7 @@ rb_method_entry_make(VALUE klass, ID mid, VALUE defined_class, rb_method_visibil https://github.com/ruby/ruby/blob/trunk/vm_method.c#L813 make_method_entry_refined(klass, me); } - rb_id_table_insert(mtbl, mid, (VALUE)me); - RB_OBJ_WRITTEN(klass, Qundef, (VALUE)me); + rb_method_table_insert(klass, mtbl, mid, me); VM_ASSERT(me->def != NULL); @@ -973,6 +983,7 @@ prepare_callable_method_entry(VALUE defined_class, ID id, const rb_method_entry_ https://github.com/ruby/ruby/blob/trunk/vm_method.c#L983 } cme = rb_method_entry_complement_defined_class(me, me->called_id, defined_class); rb_id_table_insert(mtbl, id, (VALUE)cme); + RB_OBJ_WRITTEN(defined_class, Qundef, (VALUE)cme); VM_ASSERT(callable_method_entry_p(cme)); } else { -- cgit v0.10.2 -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/