Issue #11119 has been updated by Charles Nutter.


A simple rule for future Rubies might be "if you want an anonymous module/class to have a useful path-based #name, ensure the parent namespace is itself named, all parents provide the same guarantee, and the namespace hierarchy is eventually rooted in Object."

We can also avoid the unrooted case by following this rule: if at first assignment, a given namespace is not being set into an Object-rooted namespace hierarchy, it remains unnamed and unaddressable forever.

----------------------------------------
Bug #11119: Anonymous classes and modules have terrible #name and #inspect performance
https://bugs.ruby-lang.org/issues/11119#change-52321

* Author: Charles Nutter
* Status: Open
* Priority: Normal
* Assignee: 
* ruby -v: all versions
* Backport: 2.0.0: UNKNOWN, 2.1: UNKNOWN, 2.2: UNKNOWN
----------------------------------------
MRI lazily determines the name of a class or module by walking all defined constants starting from `Object` and looking for the namespace in question. This allows deferring the full name calclation until the class/module is finished being defined. However, if the class or module is *never* accessible via `Object`, then this system-walking occurs for every call to `#name` or `#inspect` on the `class`/`module` and every call to the default `#inspect` on instances of the class.

A simple benchmark:

~~~ruby
require 'benchmark'

module B
  module X
  end
end

def a
  c = Class.new
  c2 = Class.new
  c.class_eval 'A = c2'
  c2.class_eval 'A = c'

  c
end

c = a
x = B::X

loop do
  puts 'named'
  puts Benchmark.measure { 1_000_000.times { x.name } }
  puts 'anon'
  puts Benchmark.measure { 1_000_000.times { c.name } }
  cobj = c.new
  puts 'anon obj'
  puts Benchmark.measure { 1_000_000.times { cobj.inspect } }
end
~~~
Results on MRI 2.2 and JRuby 1.7 HEAD:

MRI:

~~~
named
  0.210000   0.000000   0.210000 (  0.205585)
anon
 14.170000   0.050000  14.220000 ( 14.259003)
anon obj
 15.750000   0.060000  15.810000 ( 15.864806)
~~~
JRuby:

~~~
named
  0.250000   0.000000   0.250000 (  0.253000)
anon
  0.270000   0.000000   0.270000 (  0.264000)
anon obj
  0.450000   0.000000   0.450000 (  0.447000)
~~~
The effect worsens linearly with the size of the system. Running in a freshly-generated Rails app's console:

~~~
named
  0.260000   0.020000   0.280000 (  0.272182)
anon
240.900000   0.800000 241.700000 (242.384455)
anon obj
257.070000   1.110000 258.180000 (261.986562)
~~~
I believe MRI needs to give up on looking for the object after the first failed namespace traversal, or else eagerly build this name the way other implementations do (and accept some changes).



-- 
https://bugs.ruby-lang.org/