From 37e6c83609ac9d4c30ca4660ee16701e53cf82a3 Mon Sep 17 00:00:00 2001 From: Alan Wu Date: Wed, 22 Jul 2020 17:27:03 -0400 Subject: Lazily insert origins on prepend to save memory 98286e9850936e27e8ae5e4f20858cc9c13d2dde made it so that `Module#include` allocates an origin iclass on each use. Since `include` is widely used, the extra allocation can contribute significantly to memory usage. Instead of always allocating in anticipation of prepend, this change takes a different approach. The new setup inserts a origin iclass into the super chains of all the children of the module when prepend happens for the first time. rb_ensure_origin is made static again since now that adding an origin now means walking over all usages, we want to limit the number of places where we do it. --- class.c | 26 ++++++++++++++++++++------ eval.c | 3 --- internal/class.h | 1 - 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/class.c b/class.c index 8ac53ee63b..6835d2d728 100644 --- a/class.c +++ b/class.c @@ -348,6 +348,8 @@ copy_tables(VALUE clone, VALUE orig) } } +static void ensure_origin(VALUE klass); + /* :nodoc: */ VALUE rb_mod_init_copy(VALUE clone, VALUE orig) @@ -390,7 +392,7 @@ rb_mod_init_copy(VALUE clone, VALUE orig) int add_subclass; VALUE clone_origin; - rb_ensure_origin(clone); + ensure_origin(clone); clone_origin = RCLASS_ORIGIN(clone); while (p && p != orig_origin) { @@ -999,8 +1001,6 @@ include_modules_at(const VALUE klass, VALUE c, VALUE module, int search_super) struct rb_id_table *const klass_m_tbl = RCLASS_M_TBL(RCLASS_ORIGIN(klass)); VALUE original_klass = klass; - rb_ensure_origin(module); - while (module) { int superclass_seen = FALSE; struct rb_id_table *tbl; @@ -1112,8 +1112,8 @@ move_refined_method(ID key, VALUE value, void *data) } } -void -rb_ensure_origin(VALUE klass) +static void +ensure_origin(VALUE klass) { VALUE origin = RCLASS_ORIGIN(klass); if (origin == klass) { @@ -1132,9 +1132,10 @@ void rb_prepend_module(VALUE klass, VALUE module) { int changed = 0; + bool klass_had_no_origin = RCLASS_ORIGIN(klass) == klass; ensure_includable(klass, module); - rb_ensure_origin(klass); + ensure_origin(klass); changed = include_modules_at(klass, klass, module, FALSE); if (changed < 0) rb_raise(rb_eArgError, "cyclic prepend detected"); @@ -1143,7 +1144,20 @@ rb_prepend_module(VALUE klass, VALUE module) } if (RB_TYPE_P(klass, T_MODULE)) { rb_subclass_entry_t *iclass = RCLASS_EXT(klass)->subclasses; + VALUE klass_origin = RCLASS_ORIGIN(klass); + struct rb_id_table *klass_m_tbl = RCLASS_M_TBL(klass); + struct rb_id_table *klass_origin_m_tbl = RCLASS_M_TBL(klass_origin); while (iclass) { + if (klass_had_no_origin && klass_origin_m_tbl == RCLASS_M_TBL(iclass->klass)) { + // backfill an origin iclass to handle refinements and future prepends + rb_id_table_foreach(RCLASS_M_TBL(iclass->klass), clear_module_cache_i, (void *)iclass->klass); + RCLASS_M_TBL(iclass->klass) = klass_m_tbl; + VALUE origin = rb_include_class_new(klass_origin, RCLASS_SUPER(iclass->klass)); + RCLASS_SET_SUPER(iclass->klass, origin); + RCLASS_SET_INCLUDER(origin, RCLASS_INCLUDER(iclass->klass)); + RCLASS_SET_ORIGIN(iclass->klass, origin); + RICLASS_SET_ORIGIN_SHARED_MTBL(origin); + } include_modules_at(iclass->klass, iclass->klass, module, FALSE); iclass = iclass->next; } diff --git a/eval.c b/eval.c index 52f3cc4e9f..237d9acaa7 100644 --- a/eval.c +++ b/eval.c @@ -1551,9 +1551,6 @@ rb_mod_refine(VALUE module, VALUE klass) } ensure_class_or_module(klass); - if (RB_TYPE_P(klass, T_MODULE)) { - rb_ensure_origin(klass); - } CONST_ID(id_refinements, "__refinements__"); refinements = rb_attr_get(module, id_refinements); if (NIL_P(refinements)) { diff --git a/internal/class.h b/internal/class.h index eb4e7883f6..40938255af 100644 --- a/internal/class.h +++ b/internal/class.h @@ -119,7 +119,6 @@ VALUE rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach); VALUE rb_singleton_class_get(VALUE obj); int rb_class_has_methods(VALUE c); void rb_undef_methods_from(VALUE klass, VALUE super); -void rb_ensure_origin(VALUE klass); static inline void RCLASS_SET_ORIGIN(VALUE klass, VALUE origin); static inline void RICLASS_SET_ORIGIN_SHARED_MTBL(VALUE iclass); -- cgit v1.2.3