aboutsummaryrefslogtreecommitdiffstats
path: root/class.c
diff options
context:
space:
mode:
authorAlan Wu <XrXr@users.noreply.github.com>2020-07-22 17:27:03 -0400
committerAlan Wu <XrXr@users.noreply.github.com>2020-07-22 19:01:28 -0400
commit37e6c83609ac9d4c30ca4660ee16701e53cf82a3 (patch)
treefecc7c2b5606d9e0750898edbb9d38cce9e7dd5b /class.c
parentfdcbb288ab0faf888ed3412113e0064f3a98e1a5 (diff)
downloadruby-37e6c83609ac9d4c30ca4660ee16701e53cf82a3.tar.gz
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.
Diffstat (limited to 'class.c')
-rw-r--r--class.c26
1 files changed, 20 insertions, 6 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;
}