[#102687] [Ruby master Bug#17666] Sleep in a thread hangs when Fiber.set_scheduler is set — arjundas.27586@...

Issue #17666 has been reported by arjunmdas (arjun das).

16 messages 2021/03/02

[#102776] [Ruby master Bug#17678] Ractors do not restart after fork — knuckles@...

Issue #17678 has been reported by ivoanjo (Ivo Anjo).

8 messages 2021/03/08

[#102797] [Ruby master Feature#17684] Remove `--disable-gems` from release version of Ruby — hsbt@...

Issue #17684 has been reported by hsbt (Hiroshi SHIBATA).

17 messages 2021/03/10

[#102829] [Ruby master Bug#17718] a method paramaters object that can be pattern matched against — dsisnero@...

Issue #17718 has been reported by dsisnero (Dominic Sisneros).

9 messages 2021/03/11

[#102832] [Ruby master Misc#17720] Cirrus CI to check non-x86_64 architecture cases by own machines — jaruga@...

Issue #17720 has been reported by jaruga (Jun Aruga).

19 messages 2021/03/12

[#102850] [Ruby master Bug#17723] autoconf 2.70+ is not working with master branch — hsbt@...

Issue #17723 has been reported by hsbt (Hiroshi SHIBATA).

11 messages 2021/03/14

[#102884] [Ruby master Bug#17725] Prepend Breaks Ability to Alias — josh@...

Issue #17725 has been reported by joshuadreed (Josh Reed).

14 messages 2021/03/16

[#102914] [Ruby master Bug#17728] [BUG] Segmentation fault at 0x0000000000000000 — denthebat@...

Issue #17728 has been reported by meliborn (Denis Denis).

13 messages 2021/03/18

[#102919] [Ruby master Bug#17730] Ruby on macOS transitively links to ~150 dylibs — rickmark@...

Issue #17730 has been reported by rickmark (Rick Mark).

10 messages 2021/03/18

[#103013] [Ruby master Bug#17748] Ruby 3.0 takes a long time to resolv DNS of nonexistent domains — xdmx@...

Issue #17748 has been reported by xdmx (Eric Bloom).

8 messages 2021/03/25

[#103026] [Ruby master Feature#17749] Const source location without name — tenderlove@...

Issue #17749 has been reported by tenderlovemaking (Aaron Patterson).

10 messages 2021/03/25

[#103036] [Ruby master Misc#17751] Do these instructions (<<, +, [0..n]) modify the original string without creating copies? — cart4for1@...

Issue #17751 has been reported by stiuna (Juan Gregorio).

11 messages 2021/03/26

[#103040] [Ruby master Feature#17752] Enable -Wundef for C extensions in repository — eregontp@...

Issue #17752 has been reported by Eregon (Benoit Daloze).

23 messages 2021/03/26

[#103044] [Ruby master Feature#17753] Add Module#outer_scope — tenderlove@...

Issue #17753 has been reported by tenderlovemaking (Aaron Patterson).

31 messages 2021/03/26

[#103088] [Ruby master Feature#17760] Where we should install a header file when `gem install --user`? — muraken@...

Issue #17760 has been reported by mrkn (Kenta Murata).

11 messages 2021/03/30

[#103102] [Ruby master Feature#17762] A simple way to trace object allocation — mame@...

Issue #17762 has been reported by mame (Yusuke Endoh).

18 messages 2021/03/30

[#103105] [Ruby master Feature#17763] Implement cache for cvars — eileencodes@...

Issue #17763 has been reported by eileencodes (Eileen Uchitelle).

18 messages 2021/03/30

[ruby-core:103117] [Ruby master Feature#17763] Implement cache for cvars

From: duerst@...
Date: 2021-03-30 23:27:48 UTC
List: ruby-core #103117
Issue #17763 has been updated by duerst (Martin Dst).


Eregon (Benoit Daloze) wrote in #note-4:
> Eregon (Benoit Daloze) wrote in #note-2:

> > Also, cvars seem very unpopular (compared with ivars) so I'm not sure adding complexity would be worth while.
> 
> Yeah, definitely. Which is why I've been hesitant about optimizing class variables, it's unclear if the cost of the not-so-trivial optimization would pay off.
> It clearly seems worth it if code like the Rails logger keep using class variables though.

I'm not sure that "better optimize, because some important code keeps using this, but don't really optimize all the way, because it's no so popular" makes sense. (I'm not blaming Eregon, nor Aaron, nor Eileen, nor anybody else.)

What would it take e.g. to switch Rails logger to something else?

If the reason that some places are keeping class variables, then maybe we need to up with more convenient syntax for class instance variables.

Just trying to think out loud, sorry.

----------------------------------------
Feature #17763: Implement cache for cvars
https://bugs.ruby-lang.org/issues/17763#change-91183

* Author: eileencodes (Eileen Uchitelle)
* Status: Open
* Priority: Normal
----------------------------------------
# Introduce inline cache for class variable reads

@tenderlove and I would like to introduce an inline cache for class variable reads. We've attached a patch that introduces the cache. Class variable reads are popular in Rails applications for example, Active Record's `#logger`.

GitHub PR: https://github.com/ruby/ruby/pull/4340

## Cache Design

This patch introduces a hash table that's stored on the same class as the class variable value.

For example:

```ruby
class A
  @@foo = 1
end

class B < A
  def self.read_foo
    @@foo
  end
end
```

The above code stores the value for `@@foo` on the `A` class and stores an inline cache value on the `A` class as well. The instruction sequences for the `read_foo` method point at the CVAR inline cache entry stored on class `A`.

The lifecycle of these caches are similar to instance variable inline caches.

### Diagram of the cache:

![cvar cache](https://gist.githubusercontent.com/eileencodes/ddd95be978df27eb76543d352d516449/raw/13e969320159a4e1bff9444694a1ac198e892237/cvar%2520cache@2x%2520(6).png)


## Performance Characteristics

When class variables are read, Ruby needs to check each class in the inheritance tree to ensure that the class variable isn't set on any other classes in the tree. If the same cvar is set on a class in the inheritance tree then a "cvar overtaken" error will be raised.

Because of how cvar reads work, the more classes in the inheritance tree the more expensive a cvar read is. To demonstrate this here is a benchmark that reads a cvar from a class with 1 module, 30 modules, and 100 modules in the inheritance chain. On Ruby master 100 modules is 8.5x slower than including 1 module. With the cache, there is no performance difference between including 1 module and including 100 modules.

Benchmark script:

```ruby
require "benchmark/ips"

MODULES = ["B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "AA", "BB", "CC", "DD", "EE", "FF", "GG", "HH", "II", "JJ", "KK", "LL", "MM", "NN", "OO", "PP", "QQ", "RR", "SS", "TT", "UU", "VV", "WW", "XX", "YY", "ZZ", "AAA", "BBB", "CCC", "DDD", "EEE", "FFF", "GGG", "HHH", "III", "JJJ", "KKK", "LLL", "MMM", "NNN", "OOO", "PPP", "QQQ", "RRR", "SSS", "TTT", "UUU", "VVV", "WWW", "XXX", "YYY", "ZZZ", "AAAA", "BBBB", "CCCC", "DDDD", "EEEE", "FFFF", "GGGG", "HHHH", "IIII", "JJJJ", "KKKK", "LLLL", "MMMM", "NNNN", "OOOO", "PPPP", "QQQQ", "RRRR", "SSSS", "TTTT", "UUUU", "VVVV", "WWWW"]
class A
  @@foo = 1

  def self.foo
    @@foo
  end

  eval <<-EOM
    module #{MODULES.first}
    end

    include #{MODULES.first}
  EOM
end

class Athirty
  @@foo = 1

  def self.foo
    @@foo
  end

  MODULES.take(30).each do |module_name|
    eval <<-EOM
      module #{module_name}
      end

      include #{module_name}
    EOM
  end
end

class Ahundred
  @@foo = 1

  def self.foo
    @@foo
  end

  MODULES.each do |module_name|
    eval <<-EOM
      module #{module_name}
      end

      include #{module_name}
    EOM
  end
end

Benchmark.ips do |x|
  x.report "1 module" do
    A.foo
  end

  x.report "30 modules" do
    Athirty.foo
  end

  x.report "100 modules" do
    Ahundred.foo
  end

  x.compare!
end
```

Ruby 3.0 master:

```
Warming up --------------------------------------
            1 module     1.231M i/100ms
          30 modules   432.020k i/100ms
         100 modules   145.399k i/100ms
Calculating -------------------------------------
            1 module     12.210M (ア 2.1%) i/s -     61.553M in   5.043400s
          30 modules      4.354M (ア 2.7%) i/s -     22.033M in   5.063839s
         100 modules      1.434M (ア 2.9%) i/s -      7.270M in   5.072531s

Comparison:
            1 module: 12209958.3 i/s
          30 modules:  4354217.8 i/s - 2.80x  (ア 0.00) slower
         100 modules:  1434447.3 i/s - 8.51x  (ア 0.00) slower
```

Ruby 3.0 with cvar cache:

```
Warming up --------------------------------------
            1 module     1.641M i/100ms
          30 modules     1.655M i/100ms
         100 modules     1.620M i/100ms
Calculating -------------------------------------
            1 module     16.279M (ア 3.8%) i/s -     82.038M in   5.046923s
          30 modules     15.891M (ア 3.9%) i/s -     79.459M in   5.007958s
         100 modules     16.087M (ア 3.6%) i/s -     81.005M in   5.041931s

Comparison:
            1 module: 16279458.0 i/s
         100 modules: 16087484.6 i/s - same-ish: difference falls within error
          30 modules: 15891406.2 i/s - same-ish: difference falls within error
```

### Rails Application Benchmarks

We also benchmarked `ActiveRecord::Base.logger` since `logger` is a cvar and there are 63 modules in the inheritance chain. This is an example of a real-world improvement to Rails applications.

Benchmark:

```ruby
require "benchmark/ips"
require_relative "config/environment"

Benchmark.ips do |x|
  x.report "logger" do
    ActiveRecord::Base.logger
  end
end
```

Ruby 3.0 master:

```
Warming up --------------------------------------
              logger   155.251k i/100ms
Calculating -------------------------------------
```

Ruby 3.0 with cvar cache:

```
Warming up --------------------------------------
              logger     1.546M i/100ms
Calculating -------------------------------------
              logger     14.857M (ア 4.8%) i/s -     74.198M in   5.006202s
```

We also measured database queries in Rails and with the cvar cache they are about ~9% faster.

Benchmark code:

```ruby
class BugTest < Minitest::Test                                                                                                                               
  def test_association_stuff                                                                                                                                 
    post = Post.create!                                                                                                                                      
                                                                                                                                                             
    Benchmark.ips do |x|                                                                                                                                     
      x.report "query" do                                                                                                                                    
        Post.first                                                                                                                                           
      end                                                                                                                                                    
    end                                                                                                                                                      
  end                                                                                                                                                        
end                                                                                                                                                          
```

Ruby 3.0 master / Rails 6.1:

```
Warming up --------------------------------------
               query   790.000  i/100ms
Calculating -------------------------------------
               query      7.601k (ア 3.8%) i/s -     38.710k in   5.100534s
```

Ruby 3.0 cvar cache / Rails 6.1:

```
Warming up --------------------------------------
               query   731.000  i/100ms
Calculating -------------------------------------
               query      7.089k (ア 3.3%) i/s -     35.819k in   5.058215s
```



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

Unsubscribe: <mailto:ruby-core-request@ruby-lang.org?subject=unsubscribe>
<http://lists.ruby-lang.org/cgi-bin/mailman/options/ruby-core>

In This Thread