Slide 1

Slide 1 text

Improving CVAR performance in Ruby 3.1 EILEEN M. UCHITELLE | @eileencodes

Slide 2

Slide 2 text

Thank You RubyConf organizers, staff, & attendees —

Slide 3

Slide 3 text

Eileen M. Uchitelle @eileencodes

Slide 4

Slide 4 text

Core Team Member of Ruby on Rails

Slide 5

Slide 5 text

f Principal Software Engineer at GitHub

Slide 6

Slide 6 text

Improving CVAR performance in Ruby 3.1

Slide 7

Slide 7 text

What is a CVAR?

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

How does CVAR inheritance work?

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

IVAR inheritance > Dog.new.name => "Arya" > Dog.new.owner => "Eileen" > Puppy.new.name => "Sansa" > Puppy.new.owner => nil

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

CVAR inheritance > Dog.owner => "Eileen" > Puppy.owner => "Eileen"

Slide 16

Slide 16 text

CVAR inheritance > Dog.owner => "Eileen" > Puppy.owner => "Eileen" > Dog.name => "Sansa"

Slide 17

Slide 17 text

CVAR inheritance > Dog.owner => "Eileen" > Puppy.owner => "Eileen" > Dog.name => "Sansa" > Puppy.name => "Sansa" 🤔

Slide 18

Slide 18 text

Puppy Object Dog Kernel BasicObject

Slide 19

Slide 19 text

Puppy Dog Object Kernel BasicObject Stores @@name @@owner

Slide 20

Slide 20 text

@@name "Sansa" RCLASS_IV_TBL Puppy Dog

Slide 21

Slide 21 text

Understanding CVAR Overtaken

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

CVAR Overtaken > Puppy.name => class variable @@name of Puppy is overtaken by Dog

Slide 25

Slide 25 text

The deeper the inheritance chain the slower CVAR access is

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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!

Slide 32

Slide 32 text

Are CVARs really that common?

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

CVARs in Rails > ActiveRecord::Base.ancestors.count => 73 > ActionController::Base.ancestors.count => 71 > ActiveJob.ancestors.count => 29

Slide 36

Slide 36 text

Building a cache for CVARs

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

@@name "Sansa" RCLASS_IV_TBL RCLASS_CVC_TBL Dog

Slide 39

Slide 39 text

@@name "Sansa" RCLASS_IV_TBL @@name RCLASS_CVC_TBL Dog

Slide 40

Slide 40 text

@@name "Sansa" RCLASS_IV_TBL @@name Inline cache RCLASS_CVC_TBL Dog

Slide 41

Slide 41 text

@@name "Sansa" RCLASS_IV_TBL @@name Inline cache RCLASS_CVC_TBL Dog pointer global_cvar_state Dog

Slide 42

Slide 42 text

@@name "Sansa" RCLASS_IV_TBL @@name Inline cache RCLASS_CVC_TBL Dog pointer global_cvar_state Dog Puppy

Slide 43

Slide 43 text

Puppy.name

Slide 44

Slide 44 text

Puppy.name global_cvar_state == GET_GLOBAL_CVAR_STATE()

Slide 45

Slide 45 text

Puppy.name Dog global_cvar_state == GET_GLOBAL_CVAR_STATE()

Slide 46

Slide 46 text

Dog Puppy.name global_cvar_state == GET_GLOBAL_CVAR_STATE() @@name "Sansa" RCLASS_IV_TBL

Slide 47

Slide 47 text

S CVAR Cache PR github.com/ruby/ruby/pull/4340

Slide 48

Slide 48 text

Benchmarking performance

Slide 49

Slide 49 text

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!

Slide 50

Slide 50 text

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!

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

What did we learn?

Slide 54

Slide 54 text

🐞!

Slide 55

Slide 55 text

S Feature Request

Slide 56

Slide 56 text

Every OSS change has tradeoffs

Slide 57

Slide 57 text

Every OSS change has tradeoffs

Slide 58

Slide 58 text

Tradeoffs Increased complexity

Slide 59

Slide 59 text

Tradeoffs Encourages more usage

Slide 60

Slide 60 text

Tradeoffs Increased maintenance burden

Slide 61

Slide 61 text

Every OSS change is a negotiation

Slide 62

Slide 62 text

Negotiation CVARs aren't going anywhere

Slide 63

Slide 63 text

Negotiation Demonstrate real-world improvements

Slide 64

Slide 64 text

Negotiation Improve all applications by upgrading

Slide 65

Slide 65 text

Let's do more of this

Slide 66

Slide 66 text

Let's do more of this By learning C

Slide 67

Slide 67 text

Let's do more of this By reading "Ruby Under a Microscope"

Slide 68

Slide 68 text

Let's do more of this By making small changes

Slide 69

Slide 69 text

Making Ruby better benefits everyone

Slide 70

Slide 70 text

Making Ruby better benefits you

Slide 71

Slide 71 text

Let's go make Ruby better, together

Slide 72

Slide 72 text

Thank You RubyConf! EILEEN M. UCHITELLE | @eileencodes