From: brodock@... Date: 2018-01-17T06:16:05+00:00 Subject: [ruby-core:84899] [Ruby trunk Bug#11119] Anonymous classes and modules have terrible #name and #inspect performance Issue #11119 has been updated by brodock (Gabriel Mazetto). Some new benchmarks with recent MRI versions: Ruby 2.3.6: ``` named 0.300000 0.010000 0.310000 ( 0.305285) anon 31.550000 0.120000 31.670000 ( 32.200161) anon obj 33.820000 0.180000 34.000000 ( 34.864390) ``` Ruby 2.4.1: ``` named 0.110000 0.000000 0.110000 ( 0.110127) anon 21.670000 0.110000 21.780000 ( 22.361176) anon obj 23.370000 0.180000 23.550000 ( 24.838731) ``` Ruby 2.5.0: ``` named 0.358641 0.001790 0.360431 ( 0.371241) anon 67.509984 0.269998 67.779982 ( 69.150504) anon obj 69.621064 0.212278 69.833342 ( 70.606355) ``` This suggests it got better in 2.4.x branch but worst in 2.5.x ---------------------------------------- Bug #11119: Anonymous classes and modules have terrible #name and #inspect performance https://bugs.ruby-lang.org/issues/11119#change-69605 * Author: headius (Charles Nutter) * Status: Open * Priority: Normal * Assignee: * Target version: * 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/ Unsubscribe: