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

Performance Optimization 101 for Ruby developers

Performance Optimization 101 for Ruby developers

Nihad Abbasov

May 19, 2019
Tweet

More Decks by Nihad Abbasov

Other Decks in Programming

Transcript

  1. What is performance optimization process of modifying a software system

    to make some aspect of it work more efficiently or use fewer resources
  2. Why is performance important if your application loads and runs

    slowly, it is more likely to be abandoned by your users
  3. 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
  4. Use #map instead of #each + push # slow array

    = [] [*1..100].each { |e| array.push e } # fast array = [*1..100].map { |e| e }
  5. Use #flat_map instead of #map + flatten # slow [*1..100].map

    { |e| [e, e] }.flatten # fast [*1..100].flat_map { |e| [e, e] }
  6. Use #reverse_each instead of #reverse + each # slow [*1..100].reverse.each

    { |e| e } # fast [*1..100].reverse_each { |e| e }
  7. 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 }
  8. 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 }
  9. Use #detect instead of #select + first # slow [*1..100].select

    { |e| e == 20 }.first # fast [*1..100].detect { |e| e == 20 }
  10. 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? }
  11. Use #each_key instead of #keys + each # slow HASH.keys.each

    { |k| k } # fast HASH.each_key { |k| k } HASH = Hash[*('aa'..'zz')]
  12. Use #key? instead of #keys + include? # slow HASH.keys.include?

    'zz' # fast HASH.key? 'zz' HASH = Hash[*('aa'..'zz')]
  13. Use #value? instead of #values + include? # slow HASH.values.include?

    'zz' # fast HASH.value? 'zz' HASH = Hash[*('aa'..'zz')]
  14. Make objects immutable with #freeze # slow string = 'Hello'.freeze

    10.times { puts string } # slow string = 'Hello' 10.times { puts string }
  15. Use Hash instead of OpenStruct # slow require 'ostruct' person

    = OpenStruct.new person.name = 'John' person.name # fast person = {} person[:name] = 'John' person[:name]
  16. 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)
  17. 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 %>
  18. 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>
  19. 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
  20. 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))
  21. Avoid rendering with loops <% @users.each do |user| %> <%=

    render 'users/profile_card', user: user %> <% end %>
  22. 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
  23. Use modern cache control Add immutable to Cache-Control headers: location

    ~ ^/(assets|packs)/ { ... add_header Cache-Control public,immutable; }
  24. 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)
  25. 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")