Slide 1

Slide 1 text

Rails 7 . 1 n @a_matsuda

Slide 2

Slide 2 text

self 🌋 Tw: @a_matsuda 🌋 GH: amatsuda 🌋 Rails 🌋 Asakusa.rb 🌋 RubyKaigi

Slide 3

Slide 3 text

MAGMA City!?

Slide 4

Slide 4 text

Ruby on Rails

Slide 5

Slide 5 text

No content

Slide 6

Slide 6 text

Framework Benchmarks

Slide 7

Slide 7 text

jeremyevans/r 1 0 k

Slide 8

Slide 8 text

jeremyevans/r 1 0 k $ rake bench graphs R10K_APPS="rails roda" /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' rails 1 10 /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' rails 2 10 /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' rails 3 10 /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' rails 4 10 /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' roda 1 10 /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' roda 2 10 /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' roda 3 10 /Users/a_matsuda/.rbenv/versions/3.3.0-dev/bin/ruby builder.rb '' roda 4 10 running apps/rails_1_10.rb, pass 1, 11181 requests/second running apps/rails_1_10.rb, pass 2, 11274 requests/second running apps/rails_1_10.rb, pass 3, 10911 requests/second running apps/rails_2_10.rb, pass 1, 9691 requests/second running apps/rails_2_10.rb, pass 2, 10756 requests/second running apps/rails_2_10.rb, pass 3, 10630 requests/second running apps/rails_3_10.rb, pass 1, 10071 requests/second running apps/rails_3_10.rb, pass 2, 10211 requests/second running apps/rails_3_10.rb, pass 3, 10261 requests/second running apps/rails_4_10.rb, pass 1, 9377 requests/second running apps/rails_4_10.rb, pass 2, 9342 requests/second running apps/rails_4_10.rb, pass 3, 9440 requests/second running apps/roda_1_10.rb, pass 1, 431760 requests/second running apps/roda_1_10.rb, pass 2, 428678 requests/second running apps/roda_1_10.rb, pass 3, 433585 requests/second running apps/roda_2_10.rb, pass 1, 240096 requests/second running apps/roda_2_10.rb, pass 2, 236359 requests/second running apps/roda_2_10.rb, pass 3, 239348 requests/second running apps/roda_3_10.rb, pass 1, 178426 requests/second running apps/roda_3_10.rb, pass 2, 184530 requests/second running apps/roda_3_10.rb, pass 3, 185453 requests/second running apps/roda_4_10.rb, pass 1, 121037 requests/second running apps/roda_4_10.rb, pass 2, 144010 requests/second running apps/roda_4_10.rb, pass 3, 141795 requests/second

Slide 9

Slide 9 text

(Roda )

Slide 10

Slide 10 text

🌋 Roda 40

Slide 11

Slide 11 text

Rails 


Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

DB 🌋 Rails

Slide 14

Slide 14 text

No content

Slide 15

Slide 15 text

Rails

Slide 16

Slide 16 text

Really?

Slide 17

Slide 17 text

Rails

Slide 18

Slide 18 text

No content

Slide 19

Slide 19 text

No content

Slide 20

Slide 20 text

Rails 


Slide 21

Slide 21 text

No content

Slide 22

Slide 22 text

Rails

Slide 23

Slide 23 text

🌋

Slide 24

Slide 24 text

No 🌋 🌋 ⾒ 🌋

Slide 25

Slide 25 text

🌋 🤔

Slide 26

Slide 26 text

No content

Slide 27

Slide 27 text

No content

Slide 28

Slide 28 text

(1) 🌋 Nate Asakusa.rb

Slide 29

Slide 29 text

Nate Asakusa.rb 🌋 2022 5 🌋 Puma Nate Asakusa meetup 🌋 🌋 🌋 Rails 🌋 upstream

Slide 30

Slide 30 text

(2) 🌋 ☠

Slide 31

Slide 31 text

🌋 2022 11 🌋 🦴 🌋 🌋 push 🌋 Ruby

Slide 32

Slide 32 text

(3) 🌋 


Slide 33

Slide 33 text

🌋 nobu 🌋 OSS 
 🙏

Slide 34

Slide 34 text

No content

Slide 35

Slide 35 text

🌋 🌋 🌋

Slide 36

Slide 36 text

🌋

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

🌋 Object Allocations

Slide 39

Slide 39 text

Object Allocations 🌋 🌋 GC

Slide 40

Slide 40 text

Object Allocations 🌋 Object.new 🌋 '' [] {}

Slide 41

Slide 41 text

🌋 Rails

Slide 42

Slide 42 text

[199ec16d-cd73-4d9f-9941-a6754b86fcc3] Completed 200 OK in 2ms (Allocations: 249)

Slide 43

Slide 43 text

🌋 Zero Allocation 🌋 ( )

Slide 44

Slide 44 text

No content

Slide 45

Slide 45 text

How Do We Measure? 🌋 Rails : 7.0.4.2 (current newest stable) 🌋 production env 🌋 ObjectSpace GC.stat

Slide 46

Slide 46 text

module Rails def self.mem # ͳΜ͔ ObjectSpace.count_objects ͕ࢥͬͨͱ͓ΓͷڍಈͰ͸ͳ͔ͬͨͷͰྗٕͰ before = GC.stat(:total_allocated_objects) result = yield after = GC.stat(:total_allocated_objects) puts "total_allocated: #{after - before - 1}" result ennd

Slide 47

Slide 47 text

🌋 $ rails r 'Rails.mem { 1 0 0 .times { a = [] } }' 🌋 total_allocated: 1 0 0

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

Rails 🌋 web Rack

Slide 50

Slide 50 text

No content

Slide 51

Slide 51 text

Rails::Engine Rails::Engine.prepend( Module.new { def call(*) Rails.mem do super end end } )

Slide 52

Slide 52 text

🌋 GC

Slide 53

Slide 53 text

GC.disable Rails.application.config.to_prepare do GC.start GC.disable end

Slide 54

Slide 54 text

scaffold show $ rails g scaffold post title $ rails db:migrate $ rails r 'Post.create! title: "Post1"'

Slide 55

Slide 55 text

🌋 total_allocated: 1 7 5 7

Slide 56

Slide 56 text

No content

Slide 57

Slide 57 text


 🌋

Slide 58

Slide 58 text

🌋 SamSaffron/memory_profiler 🌋 ko 1 /allocation_tracer

Slide 59

Slide 59 text

SamSaffron/memory_profiler

Slide 60

Slide 60 text


 module Rails def self.memory_profiler result = nil MemoryProfiler.report { result = yield }.pretty_print(allocated_strings: 100, normalize_paths: true) result ennd

Slide 61

Slide 61 text


 🌋 allocated objects by location 🌋 allocated objects by class 🌋 Allocated String Report 🌋

Slide 62

Slide 62 text

show 🌋

Slide 63

Slide 63 text

No content

Slide 64

Slide 64 text


 🌋

Slide 65

Slide 65 text

🌋 
 🌋

Slide 66

Slide 66 text


 🌋 view render render 🌋 Action View template 


Slide 67

Slide 67 text

show def show render html: 'Hello' end

Slide 68

Slide 68 text

🌋 Total allocated: 6 5 1 3 5 bytes ( 7 8 2 objects)

Slide 69

Slide 69 text

set_post 🌋 @post 🌋 Active Record

Slide 70

Slide 70 text

✂ show - before_action :set_post, only: %i[ show edit update destroy ] + before_action :set_post, only: %i[ edit update destroy ]

Slide 71

Slide 71 text

🌋 Total allocated: 5 9 0 4 7 bytes ( 7 0 1 objects)

Slide 72

Slide 72 text

view render head :ok

Slide 73

Slide 73 text

No content

Slide 74

Slide 74 text

🌋 5 3 3 0 2 bytes ( 6 3 4 objects)

Slide 75

Slide 75 text

No content

Slide 76

Slide 76 text

Rails 
 🌋 Rack 🌋 Rack Rails 🌋 action

Slide 77

Slide 77 text

Rack 🌋 Rails::Engine#call 


Slide 78

Slide 78 text

Rack Rails module Rails class Prof def initialize(app) = @app = app def call(env) = Rails.memory_profiler { @app.call(env) } end application.config.middleware.use Prof end

Slide 79

Slide 79 text

action class ApplicationController < ActionController::Base def send_action(*) p request.path Rails.memory_profiler do super ennnd

Slide 80

Slide 80 text

🌋

Slide 81

Slide 81 text

🌋 2 0 7 2 bytes ( 3 4 objects)

Slide 82

Slide 82 text

No content

Slide 83

Slide 83 text

No content

Slide 84

Slide 84 text

🌋 loop do 🌋 🌋 & 🌋 🌋 end

Slide 85

Slide 85 text

No content

Slide 86

Slide 86 text

Array 
 # actionview/lib/action_view/lookup_context.rb - if values == [:js] + if (values.length == 1) && (values[0] == :js)

Slide 87

Slide 87 text

Range - path[(i + 2)..-1] + path[(i + 2), path.length]

Slide 88

Slide 88 text

Request # actionpack/lib/action_dispatch/middleware/show_exceptions.rb def call(env) - request = ActionDispatch::Request.new env @app.call(env) rescue Exception => exception + request = ActionDispatch::Request.new env if request.show_exceptions? render_exception(request, exception) else

Slide 89

Slide 89 text

String#% 
 Array # railties/lib/rails/rack/logger.rb def started_request_message(request) # :doc: - 'Started %s "%s" for %s at %s' % [ + sprintf('Started %s "%s" for %s at %s', request.raw_request_method, request.filtered_path, request.remote_ip, - Time.now.to_default_s ] + Time.now.to_default_s)

Slide 90

Slide 90 text

Rack middleware env 
 Array # actionpack/lib/action_dispatch/journey/router.rb req.path_parameters = tmp_params - status, headers, body = route.app.serve(req) + _, headers, _ = response = route.app.serve(req) if "pass" == headers["X-Cascade"] req.script_name = script_name @@ -56,7 +56,7 @@ def serve(req) next end - return [status, headers, body] + return response end

Slide 91

Slide 91 text

No content

Slide 92

Slide 92 text


 🌋

Slide 93

Slide 93 text

🌋 Rails 1 🌋

Slide 94

Slide 94 text

🌋 🌋

Slide 95

Slide 95 text

🌋 CPU 🌋 5

Slide 96

Slide 96 text

Rails 🌋 🌋 Active Support Action View

Slide 97

Slide 97 text

7.0 edge Rails (to be 7 . 1 ) push 31ddd41e58 a81be79d4b 7a63a7a668 13783f36ad 4c23742a13 8b2f57dc6f bd11e520a2 19a6979cfc e0936d99a3 ae569eaef8 3d00c8b97f 3ade331e75 ca0d6521b1 0671acfeea c9875d31cc d247f491a4 3ba079526b 5653d7d56d aa73d1ad52 076003d8e6 ffdbf17191 bfb0a6c211 a1c4aa87b3 d1461cdb61 6287c109d3 341b30be2e f517eefe14 afa47b93a1 c49b8270e1 f6dbed5de4 2b33690bba b378293c0d 946bc94e82 4ba87aae1f 2ead5132ea bcfdfefc08 d15acd7ba6 ee94a5c34b a18d90e7cd dce7c1cd7c 663e8e1ccd 9e39c8a721 7d0a788167 4f61d46348 c5af4f4505 8317fffb85 e28e2f784e 9f141a423d 4732c91d14 a790203408 15ab7223c7 055f71cece 66386d3b4c 41c2c26dc6 974de71036 45dd422901 8b617e224b 01001028df 4fbc4bbe43 6576eec6a8 3baffd31be b9beb3eee1 56333f3c69 c989a2908e 26f51f36fa 905c720c2c bc3251f1be 78599ba1e8 59728911e4 b368c68c6a 4e7620b110 9a8d2de95b 351e726be5 cac0e04313 b9fe288d6d a289f4c127 c73eafa634 60ffaac2e9 d8c05043c3 bdd090abf6 c0f16c16a3 9c66072b97 41b3e61735

Slide 98

Slide 98 text

1 80 
 🌋

Slide 99

Slide 99 text


 PR 
 🌋 real world app 


Slide 100

Slide 100 text

🌋 0.1% 50 5%

Slide 101

Slide 101 text

main push 🌋 🌋 Rails

Slide 102

Slide 102 text

main push 
 4 CI 
 🌋 ( ) 🌋

Slide 103

Slide 103 text

rack logger ipaddr 
 話

Slide 104

Slide 104 text


 🌋 Final Fight

Slide 105

Slide 105 text

edge Rails 
 🌋 1 6 7 2 bytes ( 2 5 objects)

Slide 106

Slide 106 text

Rails

Slide 107

Slide 107 text


 ⾒ 🌋 memory_profiler 
 🤔

Slide 108

Slide 108 text


 🌋

Slide 109

Slide 109 text

Rails 
 class C def initialize = @x = nil delegate :hash, to: :@x def hash2() = @x.hash end c = C.new p hash_via_delegate: c.hash Rails.memory_profiler { c.hash } #=> 1 p hash_via_method_call: c.hash2 Rails.memory_profiler { c.hash2 } #=> 0

Slide 110

Slide 110 text

delegate 


Slide 111

Slide 111 text

🌋 delegate *args, **kwargs, &block 🌋 *args Array **kwargs Hash 🌋 ... Array Hash 🌋 delegate ⾒

Slide 112

Slide 112 text


 🌋 https://bugs.ruby-lang.org/ issues/ 1 9 1 6 5

Slide 113

Slide 113 text


 delegate 
 🌋 1 5 5 2 bytes ( 2 2 objects)

Slide 114

Slide 114 text

allocated objects by location 5 actionpack/lib/action_dispatch/http/response.rb:429 4 actionpack/lib/action_dispatch/http/response.rb:428 3 rack-3.0.4.2/lib/rack/headers.rb:151 3 actionpack/lib/action_dispatch/http/response.rb:443 2 rack-3.0.4.2/lib/rack/headers.rb:31 1 actionpack/lib/action_controller/metal.rb:224 1 actionpack/lib/action_dispatch/http/mime_type.rb:149 1 actionpack/lib/action_dispatch/http/response.rb:438 1 actionpack/lib/action_dispatch/http/response.rb:468 1 kagoshima02/app/controllers/application_controller.rb:6

Slide 115

Slide 115 text

🌋 CONTENT_TYPE Match 🌋 ContentTypeHeader 🌋 Rack request header key String 🌋 charset downcase String 🌋 CONTENT_TYPE value String 🌋 Rack body Array 🌋 MimeType Symbol lookup to_s String ( Symbol GC ) 🌋 body Buffer 🌋 send_action splat Array

Slide 116

Slide 116 text

push ⾒

Slide 117

Slide 117 text

🌋 CONTENT_TYPE Match 🌋 ContentTypeHeader 🌋 charset downcase String 🌋 CONTENT_TYPE value String 🌋 Rack body Array 🌋 body Buffer 🌋 send_action splat Array

Slide 118

Slide 118 text

allocated objects by location ----------------------------------- 5 actionpack/lib/action_dispatch/http/response.rb:429 4 actionpack/lib/action_dispatch/http/response.rb:428 3 actionpack/lib/action_dispatch/http/response.rb:443 1 actionpack/lib/action_controller/metal.rb:224 1 actionpack/lib/action_dispatch/http/response.rb:438 1 actionpack/lib/action_dispatch/http/response.rb:468 1 kagoshima02/app/controllers/application_controller.rb:6

Slide 119

Slide 119 text

head :ok Final Result 🌋 1 3 1 2 bytes ( 1 6 objects)

Slide 120

Slide 120 text

Rack middleware 🌋 2 9 9 3 8 bytes ( 3 7 7 objects)

Slide 121

Slide 121 text

🌋 Rack::Static 🌋 Rack::Static

Slide 122

Slide 122 text

config.public_file_server.enab led = false 🌋 2 4 1 4 6 bytes ( 2 9 6 objects)

Slide 123

Slide 123 text

🌋 ParameterFilter 🌋 URL 🌋 IP 🌋 Monitor 🌋 🌋

Slide 124

Slide 124 text

ParameterFilter

Slide 125

Slide 125 text

🌋 2 2 0 5 4 bytes ( 2 6 8 objects)

Slide 126

Slide 126 text

No content

Slide 127

Slide 127 text

No content

Slide 128

Slide 128 text

No content

Slide 129

Slide 129 text

No content

Slide 130

Slide 130 text

benchmark-ips

Slide 131

Slide 131 text

Rails benchmark- ips 5 Rails::Engine.prepend( Module.new { def call(*) result = super Benchmark.ips do |x| x.report { super } end result end } )

Slide 132

Slide 132 text

head :ok IPS 🌋 7 . 0 : 1 . 9 4 8 k (± 4 . 1 %) i/s - 9 . 9 0 0 k in 5 . 0 9 1 7 1 0 s 🌋 7 . 1 : 6 . 5 0 6 k (± 9 . 0 %) i/s - 3 2 . 5 7 1 k in 5 . 0 4 7 1 5 5 s

Slide 133

Slide 133 text

render plain: 'Hello' IPS 🌋 7 . 0 : 1 . 8 4 1 k (± 3 . 2 %) i/s - 9 . 2 0 4 k in 5 . 0 0 3 8 4 9 s 🌋 7 . 1 : 5 . 7 6 1 k (± 8 . 2 %) i/s - 2 8 . 9 4 4 k in 5 . 0 5 9 8 1 7 s

Slide 134

Slide 134 text

No content

Slide 135

Slide 135 text

"Rails 7 . 1 n "

Slide 136

Slide 136 text


 n = 3 4

Slide 137

Slide 137 text

Action Controller

Slide 138

Slide 138 text

Action View & 
 Active Record

Slide 139

Slide 139 text

To Be Continued...

Slide 140

Slide 140 text

end