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
AI Schema Enrichment for your Oracle AI Database
thatjeffsmith
0
250
AtCoder Conference 2025
shindannin
0
1k
MUSUBIXとは
nahisaho
0
130
IFSによる形状設計/デモシーンの魅力 @ 慶應大学SFC
gam0022
1
300
Spinner 軸ズレ現象を調べたらレンダリング深淵に飲まれた #レバテックMeetup
bengo4com
1
230
dchart: charts from deck markup
ajstarks
3
990
CSC307 Lecture 06
javiergs
PRO
0
680
AIで開発はどれくらい加速したのか?AIエージェントによるコード生成を、現場の評価と研究開発の評価の両面からdeep diveしてみる
daisuketakeda
1
970
Apache Iceberg V3 and migration to V3
tomtanaka
0
150
OCaml 5でモダンな並列プログラミングを Enjoyしよう!
haochenx
0
130
Fluid Templating in TYPO3 14
s2b
0
130
Vibe codingでおすすめの言語と開発手法
uyuki234
0
220
Featured
See All Featured
The World Runs on Bad Software
bkeepers
PRO
72
12k
Code Reviewing Like a Champion
maltzj
527
40k
How to Ace a Technical Interview
jacobian
281
24k
The agentic SEO stack - context over prompts
schlessera
0
630
Become a Pro
speakerdeck
PRO
31
5.8k
Color Theory Basics | Prateek | Gurzu
gurzu
0
190
Java REST API Framework Comparison - PWX 2021
mraible
34
9.1k
HDC tutorial
michielstock
1
360
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.4k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
2k
Thoughts on Productivity
jonyablonski
74
5k
Large-scale JavaScript Application Architecture
addyosmani
515
110k
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]