Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Performance Optimization 101 for Ruby developers
Search
Nihad Abbasov
May 19, 2019
Programming
1
120
Performance Optimization 101 for Ruby developers
Nihad Abbasov
May 19, 2019
Tweet
Share
More Decks by Nihad Abbasov
See All by Nihad Abbasov
Golang for Rubyists
narkoz
1
94
Other Decks in Programming
See All in Programming
Reading Rails 1.0 Source Code
okuramasafumi
0
250
はじめてのMaterial3 Expressive
ym223
2
890
個人開発で徳島大学生60%以上の心を掴んだアプリ、そして手放した話
akidon0000
1
130
基礎から学ぶ大画面対応(Learning Large-Screen Support from the Ground Up)
tomoya0x00
0
3.3k
請來的 AI Agent 同事們在寫程式時,怎麼用 pytest 去除各種幻想與盲點
keitheis
0
120
The Past, Present, and Future of Enterprise Java
ivargrimstad
0
410
プロパティベーステストによるUIテスト: LLMによるプロパティ定義生成でエッジケースを捉える
tetta_pdnt
0
3.3k
個人軟體時代
ethanhuang13
0
330
OSS開発者という働き方
andpad
5
1.7k
Namespace and Its Future
tagomoris
6
710
為你自己學 Python - 冷知識篇
eddie
1
350
AIコーディングAgentとの向き合い方
eycjur
0
280
Featured
See All Featured
What’s in a name? Adding method to the madness
productmarketing
PRO
23
3.7k
Rails Girls Zürich Keynote
gr2m
95
14k
Designing for humans not robots
tammielis
253
25k
Optimising Largest Contentful Paint
csswizardry
37
3.4k
4 Signs Your Business is Dying
shpigford
184
22k
Producing Creativity
orderedlist
PRO
347
40k
Templates, Plugins, & Blocks: Oh My! Creating the theme that thinks of everything
marktimemedia
31
2.5k
Balancing Empowerment & Direction
lara
3
620
Intergalactic Javascript Robots from Outer Space
tanoku
272
27k
Product Roadmaps are Hard
iamctodd
PRO
54
11k
How To Stay Up To Date on Web Technology
chriscoyier
790
250k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.1k
Transcript
Performance Optimization 101 for Ruby developers
whoami Nihad Abbasov github.com/narkoz
[email protected]
AGENDA - Ruby - Rails - Front-end
What is application performance indication of the time it takes
application to respond
What is performance optimization process of modifying a software system
to make some aspect of it work more efficiently or use fewer resources
Why is performance important if your application loads and runs
slowly, it is more likely to be abandoned by your users
1. Optimizing Ruby Tips and techniques for optimizing your Ruby
code
Measure code execution time Use benchmark-ips gem
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
Use #sample instead of #shuffle.first # slow [*1..100].shuffle.first # fast
[*1..100].sample
Use #map instead of #each + push # slow array
= [] [*1..100].each { |e| array.push e } # fast array = [*1..100].map { |e| e }
Use #flat_map instead of #map + flatten # slow [*1..100].map
{ |e| [e, e] }.flatten # fast [*1..100].flat_map { |e| [e, e] }
Use #reverse_each instead of #reverse + each # slow [*1..100].reverse.each
{ |e| e } # fast [*1..100].reverse_each { |e| e }
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 }
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 }
Use #detect instead of #select + first # slow [*1..100].select
{ |e| e == 20 }.first # fast [*1..100].detect { |e| e == 20 }
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? }
Use #each_key instead of #keys + each # slow HASH.keys.each
{ |k| k } # fast HASH.each_key { |k| k } HASH = Hash[*('aa'..'zz')]
Use #key? instead of #keys + include? # slow HASH.keys.include?
'zz' # fast HASH.key? 'zz' HASH = Hash[*('aa'..'zz')]
Use #value? instead of #values + include? # slow HASH.values.include?
'zz' # fast HASH.value? 'zz' HASH = Hash[*('aa'..'zz')]
Make objects immutable with #freeze # slow string = 'Hello'.freeze
10.times { puts string } # slow string = 'Hello' 10.times { puts string }
Use magic comment # frozen_string_literal: true # frozen_string_literal: true
Use Date.iso8601 instead of Date.parse # slow Date.parse('2018-03-19') # fast
Date.iso8601('2018-03-19')
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')
Use Hash instead of OpenStruct # slow require 'ostruct' person
= OpenStruct.new person.name = 'John' person.name # fast person = {} person[:name] = 'John' person[:name]
Additional resources and tools ▪ github.com/JuanitoFatas/fast-ruby ▪ github.com/DamirSvrtan/fasterer ▪ github.com/rubocop-hq/rubocop-performance
2. Optimizing Rails Tips and techniques for optimizing your Rails
application
Benchmark your application - wrk - h2load - ab -
siege
Use application monitoring metrics - scout_apm - skylight - newrelic
Check application performance profile REQUESTS PER-MINUTE CLASSIFICATION < 50 ms
Fast < 300 ms Normal > 300 ms Slow
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)
Use database queries to deal with records Don't be afraid
to use raw SQL
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 %>
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>
Use database indexes correctly User.where(email: '
[email protected]
') # migration: add_index :users,
:email, unique: true
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
Indexes for polymorphic associations add_index :comments, :user_id add_index :comments, %i[commentable_type
commentable_id]
Order records by ID User.order(id: :desc) # faster alternative to
User.order(created_at: :desc)
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))
Eliminate N+1 queries @user = User.first <% @user.comments.each %> <%=
comment.body %> <% end %>
Eliminate N+1 queries Use AR#includes @user = User.includes(:comments).first
Use materialized database views *PostgreSQL only. use scenic gem
Use limit in queries User.limit(10) Use pagination: @pagy, @records =
pagy(Post.published)
Use explain when in doubt User.where(id: [1, 2]).comments.explain
Use background processing UserMailer.welcome(@user).deliver_later VideoImportJob.perform_later(current_user)
Avoid rendering with loops <% @users.each do |user| %> <%=
render 'users/profile_card', user: user %> <% end %>
Use collection rendering <%= render partial: 'users/profile_card', collection: @users %>
Use collection caching <%= render @post.comments, cached: true %>
Minimize or suppress HTTP redirects Use HTTP Strict Transport Security
(HSTS)
Enable HSTS preload config.force_ssl = true config.ssl_options = { hsts:
{ preload: true } }
Use libvips for image processing config.active_storage.variant_processor = :vips
Reduce log writes Use gems: ▪ rails_semantic_logger ▪ lograge
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
Use a faster language ▪ Go ▪ Elixir ▪ C
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
3. Optimizing Front-end Tips and techniques for optimizing front-end of
your application
Serve compressed assets Nginx example with gzip: location ~ ^/(assets|packs)/
{ ... gzip_static on; }
Use better compression Use Brotli compression format: location ~ ^/(assets|packs)/
{ ... gzip_static on; brotli_static on; }
Use modern cache control Add immutable to Cache-Control headers: location
~ ^/(assets|packs)/ { ... add_header Cache-Control public,immutable; }
Use resource hints <link rel="dns-prefetch" href="https://assets.example.com">
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)
Use Turbolinks Or: ▪ Vuejs ▪ React ▪ Stimulus
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")
Questions? github.com/narkoz
[email protected]