Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Improving CVAR Performance in Ruby 3.1

Improving CVAR Performance in Ruby 3.1

Have you ever wondered how class variables (CVARs) in Ruby work? Would you be surprised to learn that their performance becomes increasingly worse as the inheritance chain grows? I’m excited to share that in Ruby 3.1 we fixed the performance of CVARs.

In this talk we'll look at the language design of class variables, learn about how they work, and, deep dive into how we improved their performance in Ruby 3.1 by adding a cache! We'll look at the cache design and real-world benchmarks showing how much faster they are regardless of the size of the inheritance chain.

Eileen M. Uchitelle

November 10, 2021
Tweet

More Decks by Eileen M. Uchitelle

Other Decks in Technology

Transcript

  1. What is a CVAR? class MyClass @@cvar = 1 def

    self.read_cvar @@cvar end end
  2. What is a CVAR? class MyClass @@cvar = 1 def

    self.read_cvar @@cvar end end Writer
  3. What is a CVAR? class MyClass @@cvar = 1 def

    self.read_cvar @@cvar end end Reader
  4. IVAR inheritance class Dog attr_reader :name, :owner def initialize @name

    = "Arya" @owner = "Eileen" end end class Puppy < Dog def initialize @name = "Sansa" end end
  5. IVAR inheritance > Dog.new.name => "Arya" > Dog.new.owner => "Eileen"

    > Puppy.new.name => "Sansa" > Puppy.new.owner => nil
  6. CVAR inheritance class Dog @@name = "Arya" @@owner = "Eileen"

    def self.name @@name end def self.owner @@owner end end class Puppy < Dog @@name = "Sansa" end
  7. CVAR inheritance > Dog.owner => "Eileen" > Puppy.owner => "Eileen"

    > Dog.name => "Sansa" > Puppy.name => "Sansa" 🤔
  8. CVAR Overtaken class Dog end class Puppy < Dog @@name

    = "Sansa" def self.name @@name end end
  9. CVAR Overtaken class Dog end class Puppy < Dog @@name

    = "Sansa" def self.name @@name end end class Dog @@name = "Arya" end
  10. CVAR Benchmarks MODULES = ["A", ..."WWWW"] class One @@cvar =

    1 def self.cvar @@cvar end eval <<-EOM module #{MODULES.first} end include #{MODULES.first} EOM end
  11. CVAR Benchmarks class Thirty @@cvar = 1 def self.cvar @@cvar

    end MODULES.take(30).each do |module_name| eval <<-EOM module #{module_name} end include #{module_name} EOM end end
  12. CVAR Benchmarks class OneHundred @@cvar = 1 def self.cvar @@cvar

    end MODULES.each do |module_name| eval <<-EOM module #{module_name} end include #{module_name} EOM end end
  13. CVAR Benchmarks Benchmark.ips do |x| x.report "1 module" do One.cvar

    end x.report "30 modules" do Thirty.cvar end x.report "100 modules" do OneHundred.cvar end x.compare! end
  14. CVAR Benchmarks 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
  15. S CVAR Benchmarks 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 8.5x!
  16. CVARs in Rails module ActiveRecord module Core def self.configurations=(config) @@configurations

    = ActiveRecord::DatabaseConfigs.new(config) end self.configurations = {} def self.configurations @@configurations end end end
  17. CVARs in Rails module ActiveRecord module Core mattr_accessor :logger, instance_writer:

    false mattr_accessor :verbose_query_logs, instance_writer: false, default: false mattr_accessor :schema_format, instance_writer: false, default: :ruby mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true end end
  18. S CVAR Benchmarks 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 8.5x!
  19. S CVAR Benchmarks 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 30 modules: 15891406.2 i/s No difference!
  20. S CVAR Benchmarks $ RAILS_ENV=production INTERVAL=100 WARMUP=1 BENCHMARK=10000 ruby bin/bench

    ruby 3.1.0dev (2021-06-04T00:24:57Z master 91c542ad05) [x86_64-darwin19] Warming up... Warmup: 1 requests Benchmark: 10000 requests Request per second: 615.1 [#/s] (mean) Percentage of the requests served within a certain time (ms) 50% 1.57 66% 1.68 75% 1.74 80% 1.78 90% 1.91 95% 2.06 98% 2.36 99% 2.67 100% 35.15
  21. S CVAR Benchmarks $ RAILS_ENV=production INTERVAL=100 WARMUP=1 BENCHMARK=10000 ruby bin/bench

    ruby 3.1.0dev (2021-06-04T17:40:20Z add-cache-for.. 37c96af98b) [x86_64-darwin19] Warming up... Warmup: 1 requests Benchmark: 10000 requests Request per second: 657.1 [#/s] (mean) Percentage of the requests served within a certain time (ms) 50% 1.46 66% 1.56 75% 1.63 80% 1.68 90% 1.82 95% 2.01 98% 2.28 99% 2.50 100% 35.13