Skip to content

Commit 3abdd42

Browse files
committed
Turn rb_classext_t.fields into a T_IMEMO/class_fields
This behave almost exactly as a T_OBJECT, the layout is entirely compatible. This aims to solve two problems. First, it solves the problem of namspaced classes having a single `shape_id`. Now each namespaced classext has an object that can hold the namespace specific shape. Second, it open the door to later make class instance variable writes atomics, hence be able to read class variables without locking the VM. In the future, in multi-ractor mode, we can do the write on a copy of the `fields_obj` and then atomically swap it. Considerations: - Right now the `RClass` shape_id is always synchronized, but with namespace we should likely mark classes that have multiple namespace with a specific shape flag.
1 parent 166ff18 commit 3abdd42

File tree

14 files changed

+436
-219
lines changed

14 files changed

+436
-219
lines changed

class.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -297,16 +297,8 @@ rb_class_duplicate_classext(rb_classext_t *orig, VALUE klass, const rb_namespace
297297

298298
RCLASSEXT_M_TBL(ext) = duplicate_classext_m_tbl(RCLASSEXT_M_TBL(orig), klass, dup_iclass);
299299

300-
// TODO: consider shapes for performance
301-
if (RCLASSEXT_FIELDS(orig)) {
302-
RUBY_ASSERT(!RB_TYPE_P(klass, T_ICLASS));
303-
RCLASSEXT_FIELDS(ext) = (VALUE *)st_copy((st_table *)RCLASSEXT_FIELDS(orig));
304-
rb_autoload_copy_table_for_namespace((st_table *)RCLASSEXT_FIELDS(ext), ns);
305-
}
306-
else {
307-
if (!RB_TYPE_P(klass, T_ICLASS)) {
308-
RCLASSEXT_FIELDS(ext) = (VALUE *)st_init_numtable();
309-
}
300+
if (orig->fields_obj) {
301+
RB_OBJ_WRITE(klass, &ext->fields_obj, rb_imemo_class_fields_clone(orig->fields_obj));
310302
}
311303

312304
if (RCLASSEXT_SHARED_CONST_TBL(orig)) {

common.mk

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8117,6 +8117,7 @@ imemo.$(OBJEXT): $(top_srcdir)/internal/namespace.h
81178117
imemo.$(OBJEXT): $(top_srcdir)/internal/sanitizers.h
81188118
imemo.$(OBJEXT): $(top_srcdir)/internal/serial.h
81198119
imemo.$(OBJEXT): $(top_srcdir)/internal/set_table.h
8120+
imemo.$(OBJEXT): $(top_srcdir)/internal/st.h
81208121
imemo.$(OBJEXT): $(top_srcdir)/internal/static_assert.h
81218122
imemo.$(OBJEXT): $(top_srcdir)/internal/variable.h
81228123
imemo.$(OBJEXT): $(top_srcdir)/internal/vm.h

debug_counter.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ RB_DEBUG_COUNTER(obj_imemo_parser_strterm)
315315
RB_DEBUG_COUNTER(obj_imemo_callinfo)
316316
RB_DEBUG_COUNTER(obj_imemo_callcache)
317317
RB_DEBUG_COUNTER(obj_imemo_constcache)
318+
RB_DEBUG_COUNTER(obj_imemo_class_fields)
318319

319320
RB_DEBUG_COUNTER(opt_new_hit)
320321
RB_DEBUG_COUNTER(opt_new_miss)

ext/objspace/objspace.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ count_imemo_objects(int argc, VALUE *argv, VALUE self)
504504
INIT_IMEMO_TYPE_ID(imemo_callinfo);
505505
INIT_IMEMO_TYPE_ID(imemo_callcache);
506506
INIT_IMEMO_TYPE_ID(imemo_constcache);
507+
INIT_IMEMO_TYPE_ID(imemo_class_fields);
507508
#undef INIT_IMEMO_TYPE_ID
508509
}
509510

gc.c

Lines changed: 2 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1201,7 +1201,6 @@ rb_data_free(void *objspace, VALUE obj)
12011201

12021202
struct classext_foreach_args {
12031203
VALUE klass;
1204-
bool obj_too_complex;
12051204
rb_objspace_t *objspace; // used for update_*
12061205
};
12071206

@@ -1213,12 +1212,6 @@ classext_free(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg)
12131212

12141213
rb_id_table_free(RCLASSEXT_M_TBL(ext));
12151214
rb_cc_tbl_free(RCLASSEXT_CC_TBL(ext), args->klass);
1216-
if (args->obj_too_complex) {
1217-
st_free_table((st_table *)RCLASSEXT_FIELDS(ext));
1218-
}
1219-
else {
1220-
xfree(RCLASSEXT_FIELDS(ext));
1221-
}
12221215
if (!RCLASSEXT_SHARED_CONST_TBL(ext) && (tbl = RCLASSEXT_CONST_TBL(ext)) != NULL) {
12231216
rb_free_const_table(tbl);
12241217
}
@@ -1292,8 +1285,6 @@ rb_gc_obj_free(void *objspace, VALUE obj)
12921285
case T_MODULE:
12931286
case T_CLASS:
12941287
args.klass = obj;
1295-
args.obj_too_complex = rb_shape_obj_too_complex_p(obj) ? true : false;
1296-
12971288
rb_class_classext_foreach(obj, classext_free, (void *)&args);
12981289
if (RCLASS(obj)->ns_classext_tbl) {
12991290
st_free_table(RCLASS(obj)->ns_classext_tbl);
@@ -2305,18 +2296,6 @@ classext_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg)
23052296
*size += s;
23062297
}
23072298

2308-
static void
2309-
classext_fields_hash_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg)
2310-
{
2311-
size_t *size = (size_t *)arg;
2312-
size_t count;
2313-
RB_VM_LOCKING() {
2314-
count = rb_st_table_size((st_table *)RCLASSEXT_FIELDS(ext));
2315-
}
2316-
// class IV sizes are allocated as powers of two
2317-
*size += SIZEOF_VALUE << bit_length(count);
2318-
}
2319-
23202299
static void
23212300
classext_superclasses_memsize(rb_classext_t *ext, bool prime, VALUE namespace, void *arg)
23222301
{
@@ -2354,15 +2333,6 @@ rb_obj_memsize_of(VALUE obj)
23542333
case T_MODULE:
23552334
case T_CLASS:
23562335
rb_class_classext_foreach(obj, classext_memsize, (void *)&size);
2357-
2358-
if (rb_shape_obj_too_complex_p(obj)) {
2359-
rb_class_classext_foreach(obj, classext_fields_hash_memsize, (void *)&size);
2360-
}
2361-
else {
2362-
// class IV sizes are allocated as powers of two
2363-
size += SIZEOF_VALUE << bit_length(RCLASS_FIELDS_COUNT(obj));
2364-
}
2365-
23662336
rb_class_classext_foreach(obj, classext_superclasses_memsize, (void *)&size);
23672337
break;
23682338
case T_ICLASS:
@@ -3135,10 +3105,7 @@ gc_mark_classext_module(rb_classext_t *ext, bool prime, VALUE namespace, void *a
31353105
gc_mark_internal(RCLASSEXT_SUPER(ext));
31363106
}
31373107
mark_m_tbl(objspace, RCLASSEXT_M_TBL(ext));
3138-
if (rb_shape_obj_too_complex_p(obj)) {
3139-
gc_mark_tbl_no_pin((st_table *)RCLASSEXT_FIELDS(ext));
3140-
// for the case ELSE is written in rb_gc_mark_children() because it's per RClass, not classext
3141-
}
3108+
gc_mark_internal(RCLASSEXT_FIELDS_OBJ(ext));
31423109
if (!RCLASSEXT_SHARED_CONST_TBL(ext) && RCLASSEXT_CONST_TBL(ext)) {
31433110
mark_const_tbl(objspace, RCLASSEXT_CONST_TBL(ext));
31443111
}
@@ -3218,12 +3185,6 @@ rb_gc_mark_children(void *objspace, VALUE obj)
32183185
foreach_args.objspace = objspace;
32193186
foreach_args.obj = obj;
32203187
rb_class_classext_foreach(obj, gc_mark_classext_module, (void *)&foreach_args);
3221-
3222-
if (!rb_shape_obj_too_complex_p(obj)) {
3223-
for (attr_index_t i = 0; i < RCLASS_FIELDS_COUNT(obj); i++) {
3224-
gc_mark_internal(RCLASS_PRIME_FIELDS(obj)[i]);
3225-
}
3226-
}
32273188
break;
32283189

32293190
case T_ICLASS:
@@ -3849,7 +3810,6 @@ static void
38493810
update_classext(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg)
38503811
{
38513812
struct classext_foreach_args *args = (struct classext_foreach_args *)arg;
3852-
VALUE klass = args->klass;
38533813
rb_objspace_t *objspace = args->objspace;
38543814

38553815
if (RCLASSEXT_SUPER(ext)) {
@@ -3858,16 +3818,7 @@ update_classext(rb_classext_t *ext, bool is_prime, VALUE namespace, void *arg)
38583818

38593819
update_m_tbl(objspace, RCLASSEXT_M_TBL(ext));
38603820

3861-
if (args->obj_too_complex) {
3862-
gc_ref_update_table_values_only((st_table *)RCLASSEXT_FIELDS(ext));
3863-
}
3864-
else {
3865-
// Classext is not copied in this case
3866-
for (attr_index_t i = 0; i < RCLASS_FIELDS_COUNT(klass); i++) {
3867-
UPDATE_IF_MOVED(objspace, RCLASSEXT_FIELDS(RCLASS_EXT_PRIME(klass))[i]);
3868-
}
3869-
}
3870-
3821+
UPDATE_IF_MOVED(objspace, ext->fields_obj);
38713822
if (!RCLASSEXT_SHARED_CONST_TBL(ext)) {
38723823
update_const_tbl(objspace, RCLASSEXT_CONST_TBL(ext));
38733824
}
@@ -4255,7 +4206,6 @@ rb_gc_update_object_references(void *objspace, VALUE obj)
42554206
// Continue to the shared T_CLASS/T_MODULE
42564207
case T_MODULE:
42574208
args.klass = obj;
4258-
args.obj_too_complex = rb_shape_obj_too_complex_p(obj);
42594209
args.objspace = objspace;
42604210
rb_class_classext_foreach(obj, update_classext, (void *)&args);
42614211
break;

imemo.c

Lines changed: 104 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "id_table.h"
44
#include "internal.h"
55
#include "internal/imemo.h"
6+
#include "internal/st.h"
67
#include "vm_callinfo.h"
78

89
size_t rb_iseq_memsize(const rb_iseq_t *iseq);
@@ -29,10 +30,10 @@ rb_imemo_name(enum imemo_type type)
2930
IMEMO_NAME(svar);
3031
IMEMO_NAME(throw_data);
3132
IMEMO_NAME(tmpbuf);
33+
IMEMO_NAME(class_fields);
3234
#undef IMEMO_NAME
33-
default:
34-
rb_bug("unreachable");
3535
}
36+
rb_bug("unreachable");
3637
}
3738

3839
/* =========================================================================
@@ -109,6 +110,62 @@ rb_imemo_tmpbuf_parser_heap(void *buf, rb_imemo_tmpbuf_t *old_heap, size_t cnt)
109110
return tmpbuf;
110111
}
111112

113+
static VALUE
114+
imemo_class_fields_new(VALUE klass, size_t capa)
115+
{
116+
size_t embedded_size = offsetof(struct rb_class_fields, as.embed) + capa * sizeof(VALUE);
117+
if (rb_gc_size_allocatable_p(embedded_size)) {
118+
VALUE fields = rb_imemo_new(imemo_class_fields, klass, embedded_size);
119+
RUBY_ASSERT(IMEMO_TYPE_P(fields, imemo_class_fields));
120+
return fields;
121+
}
122+
else {
123+
VALUE fields = rb_imemo_new(imemo_class_fields, klass, sizeof(struct rb_class_fields));
124+
FL_SET_RAW(fields, OBJ_FIELD_EXTERNAL);
125+
IMEMO_OBJ_FIELDS(fields)->as.external.ptr = ALLOC_N(VALUE, capa);
126+
return fields;
127+
}
128+
}
129+
130+
VALUE
131+
rb_imemo_class_fields_new(VALUE klass, size_t capa)
132+
{
133+
return imemo_class_fields_new(rb_singleton_class(klass), capa);
134+
}
135+
136+
static VALUE
137+
imemo_class_fields_new_complex(VALUE klass, size_t capa)
138+
{
139+
VALUE fields = imemo_class_fields_new(klass, sizeof(struct rb_class_fields));
140+
IMEMO_OBJ_FIELDS(fields)->as.complex.table = st_init_numtable_with_size(capa);
141+
return fields;
142+
}
143+
144+
VALUE
145+
rb_imemo_class_fields_new_complex(VALUE klass, size_t capa)
146+
{
147+
return imemo_class_fields_new_complex(rb_singleton_class(klass), capa);
148+
}
149+
150+
VALUE
151+
rb_imemo_class_fields_clone(VALUE fields_obj)
152+
{
153+
shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
154+
VALUE clone;
155+
156+
if (rb_shape_too_complex_p(shape_id)) {
157+
clone = rb_imemo_class_fields_new_complex(CLASS_OF(fields_obj), 0);
158+
st_table *src_table = rb_imemo_class_fields_complex_tbl(fields_obj);
159+
st_replace(rb_imemo_class_fields_complex_tbl(clone), src_table);
160+
}
161+
else {
162+
clone = imemo_class_fields_new(CLASS_OF(fields_obj), RSHAPE_CAPACITY(shape_id));
163+
MEMCPY(rb_imemo_class_fields_ptr(clone), rb_imemo_class_fields_ptr(fields_obj), VALUE, RSHAPE_LEN(shape_id));
164+
}
165+
166+
return clone;
167+
}
168+
112169
/* =========================================================================
113170
* memsize
114171
* ========================================================================= */
@@ -155,6 +212,14 @@ rb_imemo_memsize(VALUE obj)
155212
case imemo_tmpbuf:
156213
size += ((rb_imemo_tmpbuf_t *)obj)->cnt * sizeof(VALUE);
157214

215+
break;
216+
case imemo_class_fields:
217+
if (rb_shape_obj_too_complex_p(obj)) {
218+
size += st_memsize(IMEMO_OBJ_FIELDS(obj)->as.complex.table);
219+
}
220+
else if (FL_TEST_RAW(obj, OBJ_FIELD_EXTERNAL)) {
221+
size += RSHAPE_CAPACITY(RBASIC_SHAPE_ID(obj)) * sizeof(VALUE);
222+
}
158223
break;
159224
default:
160225
rb_bug("unreachable");
@@ -420,6 +485,27 @@ rb_imemo_mark_and_move(VALUE obj, bool reference_updating)
420485

421486
break;
422487
}
488+
case imemo_class_fields: {
489+
rb_gc_mark_and_move((VALUE *)&RBASIC(obj)->klass);
490+
491+
if (rb_shape_obj_too_complex_p(obj)) {
492+
st_table *tbl = rb_imemo_class_fields_complex_tbl(obj);
493+
if (reference_updating) {
494+
rb_gc_ref_update_table_values_only(tbl);
495+
}
496+
else {
497+
rb_mark_tbl_no_pin(tbl);
498+
}
499+
}
500+
else {
501+
VALUE *fields = rb_imemo_class_fields_ptr(obj);
502+
attr_index_t len = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
503+
for (attr_index_t i = 0; i < len; i++) {
504+
rb_gc_mark_and_move(&fields[i]);
505+
}
506+
}
507+
break;
508+
}
423509
default:
424510
rb_bug("unreachable");
425511
}
@@ -513,6 +599,17 @@ rb_cc_tbl_free(struct rb_id_table *cc_tbl, VALUE klass)
513599
rb_id_table_free(cc_tbl);
514600
}
515601

602+
static inline void
603+
imemo_class_fields_free(struct rb_class_fields *fields)
604+
{
605+
if (rb_shape_obj_too_complex_p((VALUE)fields)) {
606+
st_free_table(fields->as.complex.table);
607+
}
608+
else if (FL_TEST_RAW((VALUE)fields, OBJ_FIELD_EXTERNAL)) {
609+
xfree(fields->as.external.ptr);
610+
}
611+
}
612+
516613
void
517614
rb_imemo_free(VALUE obj)
518615
{
@@ -576,6 +673,7 @@ rb_imemo_free(VALUE obj)
576673
break;
577674
case imemo_svar:
578675
RB_DEBUG_COUNTER_INC(obj_imemo_svar);
676+
579677
break;
580678
case imemo_throw_data:
581679
RB_DEBUG_COUNTER_INC(obj_imemo_throw_data);
@@ -585,6 +683,10 @@ rb_imemo_free(VALUE obj)
585683
xfree(((rb_imemo_tmpbuf_t *)obj)->ptr);
586684
RB_DEBUG_COUNTER_INC(obj_imemo_tmpbuf);
587685

686+
break;
687+
case imemo_class_fields:
688+
imemo_class_fields_free(IMEMO_OBJ_FIELDS(obj));
689+
RB_DEBUG_COUNTER_INC(obj_imemo_class_fields);
588690
break;
589691
default:
590692
rb_bug("unreachable");

0 commit comments

Comments
 (0)