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

Rails 7.1をn倍速くした話

Rails 7.1をn倍速くした話

鹿児島Ruby会議02 の講演「Rails 7.1をn倍速くした話」のスライド https://k-ruby.com/kagoshima-rubykaigi02/ #k_ruby

Akira Matsuda

April 27, 2023
Tweet

More Decks by Akira Matsuda

Other Decks in Programming

Transcript

  1. 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
  2. How Do We Measure? 🌋 Rails : 7.0.4.2 (current newest

    stable) 🌋 production env 🌋 ObjectSpace GC.stat
  3. 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
  4. 🌋 $ rails r 'Rails.mem { 1 0 0 .times

    { a = [] } }' 🌋 total_allocated: 1 0 0
  5. scaffold show $ rails g scaffold post title $ rails

    db:migrate $ rails r 'Post.create! title: "Post1"'
  6. 
 module Rails def self.memory_profiler result = nil MemoryProfiler.report {

    result = yield }.pretty_print(allocated_strings: 100, normalize_paths: true) result ennd
  7. ✂ show - before_action :set_post, only: %i[ show edit update

    destroy ] + before_action :set_post, only: %i[ edit update destroy ]
  8. 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
  9. 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
  10. 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)
  11. 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
  12. 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
  13. 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
  14. 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
  15. 🌋 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
  16. 🌋 CONTENT_TYPE Match 🌋 ContentTypeHeader 🌋 charset downcase String 🌋

    CONTENT_TYPE value String 🌋 Rack body Array 🌋 body Buffer 🌋 send_action splat Array
  17. 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
  18. Rails benchmark- ips 5 Rails::Engine.prepend( Module.new { def call(*) result

    = super Benchmark.ips do |x| x.report { super } end result end } )
  19. 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
  20. 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
  21. end