Shink RClass when it is known they can't be namespaced

Even when namespaces are enabled, only a few core classes created
during init will eventually be namespaced.

For these it's OK to allocate a 320B slot to hold the extra namespace
stuff.

But for any class created post init, we know we'll never need the
namespace and we can fit in a 160B slot.
This commit is contained in:
Jean Boussier 2025-06-18 11:31:11 +01:00
parent ea4a53c595
commit 32ee3fab0a
2 changed files with 21 additions and 10 deletions

View file

@ -394,7 +394,7 @@ class_classext_foreach_i(st_data_t key, st_data_t value, st_data_t arg)
void void
rb_class_classext_foreach(VALUE klass, rb_class_classext_foreach_callback_func *func, void *arg) rb_class_classext_foreach(VALUE klass, rb_class_classext_foreach_callback_func *func, void *arg)
{ {
st_table *tbl = RCLASS(klass)->ns_classext_tbl; st_table *tbl = RCLASS_CLASSEXT_TBL(klass);
struct class_classext_foreach_arg foreach_arg; struct class_classext_foreach_arg foreach_arg;
if (tbl) { if (tbl) {
foreach_arg.func = func; foreach_arg.func = func;
@ -649,7 +649,11 @@ class_alloc(enum ruby_value_type type, VALUE klass)
rb_ns_subclasses_t *ns_subclasses; rb_ns_subclasses_t *ns_subclasses;
rb_subclass_anchor_t *anchor; rb_subclass_anchor_t *anchor;
const rb_namespace_t *ns = rb_definition_namespace(); const rb_namespace_t *ns = rb_definition_namespace();
size_t alloc_size = sizeof(struct RClass) + sizeof(rb_classext_t);
size_t alloc_size = sizeof(struct RClass_and_rb_classext_t);
if (!ruby_namespace_init_done) {
alloc_size = sizeof(struct RClass_namespaceable);
}
// class_alloc is supposed to return a new object that is not promoted yet. // class_alloc is supposed to return a new object that is not promoted yet.
// So, we need to avoid GC after NEWOBJ_OF. // So, we need to avoid GC after NEWOBJ_OF.

View file

@ -136,7 +136,6 @@ STATIC_ASSERT(shape_max_variations, SHAPE_MAX_VARIATIONS < (1 << (sizeof(((rb_cl
struct RClass { struct RClass {
struct RBasic basic; struct RBasic basic;
st_table *ns_classext_tbl; // ns_object -> (rb_classext_t *)
VALUE object_id; VALUE object_id;
/* /*
* If ns_classext_tbl is NULL, then the prime classext is readable (because no other classext exists). * If ns_classext_tbl is NULL, then the prime classext is readable (because no other classext exists).
@ -144,16 +143,22 @@ struct RClass {
*/ */
}; };
// Assert that classes can be embedded in heaps[2] (which has 160B slot size)
// On 32bit platforms there is no variable width allocation so it doesn't matter.
// TODO: restore this assertion after shrinking rb_classext_t
// STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass) + sizeof(rb_classext_t) <= 4 * RVALUE_SIZE || SIZEOF_VALUE < SIZEOF_LONG_LONG);
struct RClass_and_rb_classext_t { struct RClass_and_rb_classext_t {
struct RClass rclass; struct RClass rclass;
rb_classext_t classext; rb_classext_t classext;
}; };
#if SIZEOF_VALUE >= SIZEOF_LONG_LONG
// Assert that classes can be embedded in heaps[2] (which has 160B slot size)
// On 32bit platforms there is no variable width allocation so it doesn't matter.
STATIC_ASSERT(sizeof_rb_classext_t, sizeof(struct RClass_and_rb_classext_t) <= 4 * RVALUE_SIZE);
#endif
struct RClass_namespaceable {
struct RClass_and_rb_classext_t base;
st_table *ns_classext_tbl; // ns_object -> (rb_classext_t *)
};
static const uint16_t RCLASS_MAX_SUPERCLASS_DEPTH = ((uint16_t)-1); static const uint16_t RCLASS_MAX_SUPERCLASS_DEPTH = ((uint16_t)-1);
static inline bool RCLASS_SINGLETON_P(VALUE klass); static inline bool RCLASS_SINGLETON_P(VALUE klass);
@ -297,7 +302,8 @@ static inline st_table *
RCLASS_CLASSEXT_TBL(VALUE klass) RCLASS_CLASSEXT_TBL(VALUE klass)
{ {
if (FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE)) { if (FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE)) {
return RCLASS(klass)->ns_classext_tbl; struct RClass_namespaceable *ns_klass = (struct RClass_namespaceable *)klass;
return ns_klass->ns_classext_tbl;
} }
return NULL; return NULL;
} }
@ -306,7 +312,8 @@ static inline void
RCLASS_SET_CLASSEXT_TBL(VALUE klass, st_table *tbl) RCLASS_SET_CLASSEXT_TBL(VALUE klass, st_table *tbl)
{ {
RUBY_ASSERT(FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE)); RUBY_ASSERT(FL_TEST_RAW(klass, RCLASS_NAMESPACEABLE));
RCLASS(klass)->ns_classext_tbl = tbl; struct RClass_namespaceable *ns_klass = (struct RClass_namespaceable *)klass;
ns_klass->ns_classext_tbl = tbl;
} }
/* class.c */ /* class.c */