mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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]
This commit is contained in:
parent
1b34753490
commit
264e4cd04f
Notes:
git
2020-08-18 06:18:12 +09:00
6 changed files with 35 additions and 21 deletions
9
class.c
9
class.c
|
@ -911,8 +911,7 @@ rb_include_class_new(VALUE module, VALUE super)
|
|||
{
|
||||
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)
|
|||
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)
|
|||
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);
|
||||
|
|
3
eval.c
3
eval.c
|
@ -1420,8 +1420,7 @@ rb_using_refinement(rb_cref_t *cref, VALUE klass, VALUE module)
|
|||
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) {
|
||||
|
|
9
gc.c
9
gc.c
|
@ -2854,8 +2854,7 @@ obj_free(rb_objspace_t *objspace, VALUE obj)
|
|||
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)
|
|||
}
|
||||
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)
|
|||
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)) {
|
||||
|
|
|
@ -144,6 +144,12 @@ RICLASS_SET_ORIGIN_SHARED_MTBL(VALUE iclass)
|
|||
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)
|
||||
{
|
||||
|
|
4
method.h
4
method.h
|
@ -188,6 +188,8 @@ struct rb_method_definition_struct {
|
|||
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);
|
|||
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);
|
||||
|
|
25
vm_method.c
25
vm_method.c
|
@ -174,11 +174,9 @@ clear_method_cache_by_id_in_class(VALUE klass, ID mid)
|
|||
// 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)
|
|||
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
|
|||
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_
|
|||
}
|
||||
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 {
|
||||
|
|
Loading…
Reference in a new issue