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
140
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Performance Optimization 101 for Ruby developers
Nihad Abbasov
May 19, 2019
More Decks by Nihad Abbasov
See All by Nihad Abbasov
Golang for Rubyists
narkoz
1
110
Other Decks in Programming
See All in Programming
楽しそうなつよつよエンジニアと目が死んでる僕/A brilliant engineer having a blast, and dead-eyed me.
3l4l5
2
100
FDEが実現するAI駆動経営の現在地
gonta
2
280
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
210
【やさしく解説 設計編・中級 #1】一つの車に、運転手は一人 ~ある倉庫システムの事例から~
panda728
PRO
0
210
いまどきの Codex で開発する visionOS アプリの開発スタイルについて
karad
0
120
ここ半年くらいでAIに作らせたR用ツール
eitsupi
0
370
Terraform標準の組織で AWS CDKをどう使うか
mu7889yoon
1
500
Android CLI
fornewid
0
220
PHP に部分適用が来るぞ!……ところで何それ?おいしいの? #phpcon / phpcon-2026
shogogg
0
610
「人を評価する AI」の設計と実装
ryoyanara
0
180
源内ハンズオン概要編
hideg
0
150
Foundation Models frameworkで画像分析
ryodeveloper
1
600
Featured
See All Featured
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.4k
The Illustrated Children's Guide to Kubernetes
chrisshort
51
53k
How to make the Groovebox
asonas
2
2.3k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
Bridging the Design Gap: How Collaborative Modelling removes blockers to flow between stakeholders and teams @FastFlow conf
baasie
0
640
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
0
360
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
200
How to optimise 3,500 product descriptions for ecommerce in one day using ChatGPT
katarinadahlin
PRO
2
3.7k
Organizational Design Perspectives: An Ontology of Organizational Design Elements
kimpetersen
PRO
1
790
Fashionably flexible responsive web design (full day workshop)
malarkey
408
67k
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
470
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.6k
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]