[前][次][番号順一覧][スレッド一覧]

ruby-changes:62360

From: Alan <ko1@a...>
Date: Thu, 23 Jul 2020 08:01:44 +0900 (JST)
Subject: [ruby-changes:62360] 37e6c83609 (master): Lazily insert origins on prepend to save memory

https://git.ruby-lang.org/ruby.git/commit/?id=37e6c83609

From 37e6c83609ac9d4c30ca4660ee16701e53cf82a3 Mon Sep 17 00:00:00 2001
From: Alan Wu <XrXr@u...>
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.

diff --git a/class.c b/class.c
index 8ac53ee..6835d2d 100644
--- a/class.c
+++ b/class.c
@@ -348,6 +348,8 @@ copy_tables(VALUE clone, VALUE orig) https://github.com/ruby/ruby/blob/trunk/class.c#L348
     }
 }
 
+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) https://github.com/ruby/ruby/blob/trunk/class.c#L392
         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) https://github.com/ruby/ruby/blob/trunk/class.c#L1001
     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) https://github.com/ruby/ruby/blob/trunk/class.c#L1112
     }
 }
 
-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 https://github.com/ruby/ruby/blob/trunk/class.c#L1132
 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) https://github.com/ruby/ruby/blob/trunk/class.c#L1144
     }
     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 52f3cc4..237d9ac 100644
--- a/eval.c
+++ b/eval.c
@@ -1551,9 +1551,6 @@ rb_mod_refine(VALUE module, VALUE klass) https://github.com/ruby/ruby/blob/trunk/eval.c#L1551
     }
 
     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 eb4e788..4093825 100644
--- a/internal/class.h
+++ b/internal/class.h
@@ -119,7 +119,6 @@ VALUE rb_singleton_class_clone_and_attach(VALUE obj, VALUE attach); https://github.com/ruby/ruby/blob/trunk/internal/class.h#L119
 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 v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]