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
130
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
100
Other Decks in Programming
See All in Programming
なぜSQLはAIぽく見えるのか/why does SQL look AI like
florets1
0
450
AIによる高速開発をどう制御するか? ガードレール設置で開発速度と品質を両立させたチームの事例
tonkotsuboy_com
7
2k
ThorVG Viewer In VS Code
nors
0
760
OSSとなったswift-buildで Xcodeのビルドを差し替えられるため 自分でXcodeを直せる時代になっている ダイアモンド問題編
yimajo
3
610
【卒業研究】会話ログ分析によるユーザーごとの関心に応じた話題提案手法
momok47
0
190
16年目のピクシブ百科事典を支える最新の技術基盤 / The Modern Tech Stack Powering Pixiv Encyclopedia in its 16th Year
ahuglajbclajep
5
990
AI & Enginnering
codelynx
0
110
dchart: charts from deck markup
ajstarks
3
990
Package Management Learnings from Homebrew
mikemcquaid
0
210
例外処理とどう使い分ける?Result型を使ったエラー設計 #burikaigi
kajitack
16
6k
Grafana:建立系統全知視角的捷徑
blueswen
0
330
AgentCoreとHuman in the Loop
har1101
5
220
Featured
See All Featured
How People are Using Generative and Agentic AI to Supercharge Their Products, Projects, Services and Value Streams Today
helenjbeal
1
120
Leading Effective Engineering Teams in the AI Era
addyosmani
9
1.6k
DBのスキルで生き残る技術 - AI時代におけるテーブル設計の勘所
soudai
PRO
62
49k
State of Search Keynote: SEO is Dead Long Live SEO
ryanjones
0
110
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
34k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
52
Fireside Chat
paigeccino
41
3.8k
Beyond borders and beyond the search box: How to win the global "messy middle" with AI-driven SEO
davidcarrasco
1
49
Applied NLP in the Age of Generative AI
inesmontani
PRO
4
2k
Odyssey Design
rkendrick25
PRO
1
490
Connecting the Dots Between Site Speed, User Experience & Your Business [WebExpo 2025]
tammyeverts
11
820
How To Stay Up To Date on Web Technology
chriscoyier
791
250k
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]