Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Performance Optimization 101 for Ruby developers - Nihad Abbasov (ENG) | Ruby Meditation 27

58 views

Published on

Speech of Nihad Abbasov, Senior Software Engineer at Digital Classifieds, at Ruby Meditation 27, Dnipro, 19.05.2019
Slideshare -
Next conference - http://www.rubymeditation.com/

How fast is your code? Performance is crucial as your startup grows, and optimizing your application can make a huge impact on user experience. During this talk, you will learn hints, techniques and best practices for improving the overall speed of your Ruby application.

Announcements and conference materials https://www.fb.me/RubyMeditation
News https://twitter.com/RubyMeditation
Photos https://www.instagram.com/RubyMeditation
The stream of Ruby conferences (not just ours) https://t.me/RubyMeditation

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Performance Optimization 101 for Ruby developers - Nihad Abbasov (ENG) | Ruby Meditation 27

  1. 1. Performance Optimization 101 for Ruby developers
  2. 2. whoami Nihad Abbasov github.com/narkoz nihad@42na.in
  3. 3. AGENDA - Ruby - Rails - Front-end
  4. 4. What is application performance indication of the time it takes application to respond
  5. 5. What is performance optimization process of modifying a software system to make some aspect of it work more efficiently or use fewer resources
  6. 6. Why is performance important if your application loads and runs slowly, it is more likely to be abandoned by your users
  7. 7. 1. Optimizing Ruby Tips and techniques for optimizing your Ruby code
  8. 8. Measure code execution time Use benchmark-ips gem
  9. 9. Benchmark with benchmark-ips require 'benchmark/ips' Benchmark.ips do |x| x.report('addition') { 1 + 1 } x.report('addition with send') { 1.send(:+, 1) } x.compare! end
  10. 10. Use #sample instead of #shuffle.first # slow [*1..100].shuffle.first # fast [*1..100].sample
  11. 11. Use #map instead of #each + push # slow array = [] [*1..100].each { |e| array.push e } # fast array = [*1..100].map { |e| e }
  12. 12. Use #flat_map instead of #map + flatten # slow [*1..100].map { |e| [e, e] }.flatten # fast [*1..100].flat_map { |e| [e, e] }
  13. 13. Use #reverse_each instead of #reverse + each # slow [*1..100].reverse.each { |e| e } # fast [*1..100].reverse_each { |e| e }
  14. 14. Use #min_by instead of #sort_by + first # slow [*1..100].sort_by { |e| e.next }.first # fast [*1..100].min_by { |e| e.next }
  15. 15. Use #min/max instead of #sort + first/last # slow [*1..100].sort_by { |e| e.next }.last [*1..100].sort { |e| e.next }.first [*1..100].sort { |e| e.next }.last # fast [*1..100].max_by { |e| e.next } [*1..100].min { |e| e.next } [*1..100].max { |e| e.next }
  16. 16. Use #detect instead of #select + first # slow [*1..100].select { |e| e == 20 }.first # fast [*1..100].detect { |e| e == 20 }
  17. 17. Use #reverse.detect instead of #select.last # slow [*1..100].select { |e| (e % 10).zero? }.last # fast [*1..100].reverse.detect { |e| (e % 10).zero? }
  18. 18. Use #each_key instead of #keys + each # slow HASH.keys.each { |k| k } # fast HASH.each_key { |k| k } HASH = Hash[*('aa'..'zz')]
  19. 19. Use #key? instead of #keys + include? # slow HASH.keys.include? 'zz' # fast HASH.key? 'zz' HASH = Hash[*('aa'..'zz')]
  20. 20. Use #value? instead of #values + include? # slow HASH.values.include? 'zz' # fast HASH.value? 'zz' HASH = Hash[*('aa'..'zz')]
  21. 21. Make objects immutable with #freeze # slow string = 'Hello'.freeze 10.times { puts string } # slow string = 'Hello' 10.times { puts string }
  22. 22. Use magic comment # frozen_string_literal: true # frozen_string_literal: true
  23. 23. Use Date.iso8601 instead of Date.parse # slow Date.parse('2018-03-19') # fast Date.iso8601('2018-03-19')
  24. 24. Use Time.iso8601 instead of Time.parse # slow Time.parse('2018-03-21T1 1:26:50Z') # fast Time.iso8601('2018-03-21T1 1:26:50Z')
  25. 25. Use Hash instead of OpenStruct # slow require 'ostruct' person = OpenStruct.new person.name = 'John' person.name # fast person = {} person[:name] = 'John' person[:name]
  26. 26. Additional resources and tools ▪ github.com/JuanitoFatas/fast-ruby ▪ github.com/DamirSvrtan/fasterer ▪ github.com/rubocop-hq/rubocop-performance
  27. 27. 2. Optimizing Rails Tips and techniques for optimizing your Rails application
  28. 28. Benchmark your application - wrk - h2load - ab - siege
  29. 29. Use application monitoring metrics - scout_apm - skylight - newrelic
  30. 30. Check application performance profile REQUESTS PER-MINUTE CLASSIFICATION < 50 ms Fast < 300 ms Normal > 300 ms Slow
  31. 31. Use database queries to deal with records # DO: Item.order(created_at: :desc).limit(20) # DONT: Item.all.sort_by(&:created_at).reverse.first(20)
  32. 32. Use database queries to deal with records Don't be afraid to use raw SQL
  33. 33. Use select to get only the necessary data @users = User.select(:name, :email).limit(20) <% @users.each do |user| %> <%= user.name %><br> <%= user.email %> <% end %>
  34. 34. Use size on relation instead of count @messages = current_user.messages.unread <% @messages.each do |message| %> <%= message.body %> <% end %> <h2>Unread Messages: <%= @messages.size %></h2>
  35. 35. Use database indexes correctly User.where(email: 'user@example.org') # migration: add_index :users, :email, unique: true
  36. 36. Indexes for polymorphic associations class Comment belongs_to :commentable, polymorphic: true belongs_to :user end class Post has_many :comments, as: :commentable has_one :user end
  37. 37. Indexes for polymorphic associations add_index :comments, :user_id add_index :comments, %i[commentable_type commentable_id]
  38. 38. Order records by ID User.order(id: :desc) # faster alternative to User.order(created_at: :desc)
  39. 39. Use subqueries with ActiveRecord # 2 separate queries Ad.where(id: current_user.bookmarks.pluck(:ad_id)) # 1 query with subquery Ad.where(id: current_user.bookmarks.select(:ad_id))
  40. 40. Eliminate N+1 queries @user = User.first <% @user.comments.each %> <%= comment.body %> <% end %>
  41. 41. Eliminate N+1 queries Use AR#includes @user = User.includes(:comments).first
  42. 42. Use materialized database views *PostgreSQL only. use scenic gem
  43. 43. Use limit in queries User.limit(10) Use pagination: @pagy, @records = pagy(Post.published)
  44. 44. Use explain when in doubt User.where(id: [1, 2]).comments.explain
  45. 45. Use background processing UserMailer.welcome(@user).deliver_later VideoImportJob.perform_later(current_user)
  46. 46. Avoid rendering with loops <% @users.each do |user| %> <%= render 'users/profile_card', user: user %> <% end %>
  47. 47. Use collection rendering <%= render partial: 'users/profile_card', collection: @users %>
  48. 48. Use collection caching <%= render @post.comments, cached: true %>
  49. 49. Minimize or suppress HTTP redirects Use HTTP Strict Transport Security (HSTS)
  50. 50. Enable HSTS preload config.force_ssl = true config.ssl_options = { hsts: { preload: true } }
  51. 51. Use libvips for image processing config.active_storage.variant_processor = :vips
  52. 52. Reduce log writes Use gems: ▪ rails_semantic_logger ▪ lograge
  53. 53. Use faster gems ▪ github.com/SamSaffron/fast_blank ▪ github.com/ohler55/oj ▪ github.com/ohler55/ox ▪ github.com/redis/hiredis-rb ▪ github.com/ddnexus/pagy
  54. 54. Use a faster language ▪ Go ▪ Elixir ▪ C 👀
  55. 55. Additional resources and tools ▪ github.com/MiniProfiler/rack-mini-profiler ▪ github.com/gregnavis/active_record_doctor ▪ github.com/tmm1/stackprof ▪ github.com/schneems/derailed_benchmarks ▪ github.com/plentz/lol_dba ▪ github.com/flyerhzm/bullet
  56. 56. 3. Optimizing Front-end Tips and techniques for optimizing front-end of your application
  57. 57. Serve compressed assets Nginx example with gzip: location ~ ^/(assets|packs)/ { ... gzip_static on; }
  58. 58. Use better compression Use Brotli compression format: location ~ ^/(assets|packs)/ { ... gzip_static on; brotli_static on; }
  59. 59. Use modern cache control Add immutable to Cache-Control headers: location ~ ^/(assets|packs)/ { ... add_header Cache-Control public,immutable; }
  60. 60. Use resource hints <link rel="dns-prefetch" href="https://assets.example.com">
  61. 61. Use WEBP for images Supported by: ▪ Google Chrome (desktop) 17+ ▪ Google Chrome for Android version 25+ ▪ Microsoft Edge 18+ ▪ Firefox 65+ ▪ Opera 11.10+ ▪ Native web browser, Android 4.0+ (ICS)
  62. 62. Use Turbolinks Or: ▪ Vuejs ▪ React ▪ Stimulus
  63. 63. Use with caution HTTP/2 Push path = view_context.asset_path('application.css', host: '', protocol: :relative) response.set_header('Link', "<#{path}>; rel=preload; as=style")
  64. 64. Questions? github.com/narkoz nihad@42na.in

×