Thanks to visit codestin.com
Credit goes to www.slideshare.net

Ruby Performance
The Low Hanging Fruit
Hello, I’m
Bruce Werdschinski
Senior Lead Developer, Prezentt
!
Spent 5 years as the Technical Director
at GTP iCommerce
!
~2.5 years with Ruby, built hundreds of
ecommerce websites, loves SEO &
CRO
!
Mercedes enthusiast
Agenda
• Introduction to common tools
• Scenarios where we would use
those tools
• What else can we do, low
hanging fruit wise
From experience, look at common
expensive tasks
• Database operations
• Network access
• Incorrect algorithm
• Unnecessary work - Can we cache it?
Ruby benchmark
• Disable Garbage Collection first with: GC.disable
• Part of the Ruby standard library
• I prefer to use it like this:
!
time = Benchmark.realtime do
# The code I’m testing
end
benchmark-ips
• https://github.com/evanphx/benchmark-ips
• Benchmarks a blocks iterations/second.
• For short snippits of code, IPS automatically figures out how many
times to run the code to get interesting data
• Provides a standard deviation measure which is very cool
stackprof
• https://github.com/tmm1/stackprof
• For Ruby 2.1 and above
• Samples what the CPU is spending time on
• https://github.com/alisnic/stackprof-webnav is a Web UI for
stackprof dumps
• https://github.com/quirkey/stackprof-remote is middleware and CLI
for fetching and interacting with stackprof
How do we find expensive things?
• Experience / Knowledge
• Logs
• Ruby's own benchmark module
• External tools
• benchmark-ips, stackprof, time
• SaaS metrics tools
• Librato, Datadog, Skylight, New
Relic, Keen IO
Reduce the impact
• Optimise the application
• Optimise the environment
Optimise the Application
• Reduce heavy I/O
• ActiveRecord tweaks
• Caching
Reduce heavy I/O
Silly example, but it illustrates the point. Writing to the screen is more
expensive than writing to memory
!
1_000_000.times do
puts "Hello world!"
end
!
s = String.new
1_000_000.times do
s << "Hello world!n"
end
puts s
10.5 seconds vs. 6.5 seconds - But 2.28 times more memory
Reduce heavy I/O
• Same applies for database calls,
event processing, etc. One large
call is often much faster than many
little calls.
• I’ve been playing with Keen IO
lately, their demo app is at: http://
keen-gem-example.herokuapp.com/
• 50 calls, 1 item each: 20094ms
• 1 call with 50 items: 284ms
ActiveRecord
• Know your SQL
• Check logs to see what ActiveRecord is actually doing
• Lots of tools to help us
ActiveRecord sum example
I want to get a total of all to the orders in the database
Order.sum(&:total)
Order Load (49.3ms) SELECT "orders".* FROM "orders"
Order.sum(:total)
(4.0ms) SELECT SUM("orders"."total") AS sum_id FROM "orders"
ActiveRecord sum example
ActiveRecord sum example
ActiveRecord Tools
• rack-mini-profiler
• RailsPanel
• rails-footnotes
• Bullet
• Peek
rack-mini-profiler
• https://github.com/MiniProfiler/
rack-mini-profiler
• Middleware that shows a HTML
panel on each page
RailsPanel
• https://github.com/dejan/
rails_panel
• Chrome Extension that provides
information about the Rails
request. Includes Database,
rendering and total times.
Memoization
Memoization is a caching technique of storing a computed value to
avoid duplicated work by future calls.
1. Perform some work
2. Store the result of that work
3. Use the stored data the next time you need the result of that work
Memoization
A common pattern is using the conditional assignment operator: ||=
def current_user
User.find(session[:user_id])
end
Instead store the result in an instance variable by using:
def current_user
@current_user ||= User.find(session[:user_id])
end
identity_cache
https://github.com/Shopify/identity_cache
ActiveRecord cache for model objects
Extracted from Shopify
@user = User.fetch(id) instead of @user = User.find(id)
Uses an after_commit hook to expire the object from cache
Rails Cache
• Use the Dalli gem as the Rails cache store
def fetch(id)
Rails.cache.fetch("product-#{id}", expires_in: 5.minutes) do
Product.find(id)
end
end
Is Ruby the best tool?
• Sometimes…shock, horror…Ruby might not be the best tool for the
job.
• Where Ruby may not be the best fit:
• CPU bound
• Async I/O
Leibniz formula for π
1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 … = π/4
20 million iterations
CPU bound task
• Ruby: 3.78 seconds
• Rust: 0.238 seconds
Optimise the Environment
• Low hanging fruit, faster server, database, more memory
• Ruby versions
• Network topology
• Caching systems
• Web performance
Web performance
• No point shaving 1/10th of a second off your database access time
if you’ve got a 3Mb background image.
• http://gtmetrix.com/
• http://tools.pingdom.com/fpt/
No more low hanging fruit
• “Ruby Under a Microscope”
by Pat Shaughnessy
• Utilization Saturation and
Errors (USE) Method
Thank You!
Twitter: @bwerdschinski
!
Email: bruce@werdschinski.com
!
Blog: http://www.werdschinski.com

Ruby performance - The low hanging fruit

  • 1.
  • 2.
    Hello, I’m Bruce Werdschinski SeniorLead Developer, Prezentt ! Spent 5 years as the Technical Director at GTP iCommerce ! ~2.5 years with Ruby, built hundreds of ecommerce websites, loves SEO & CRO ! Mercedes enthusiast
  • 3.
    Agenda • Introduction tocommon tools • Scenarios where we would use those tools • What else can we do, low hanging fruit wise
  • 4.
    From experience, lookat common expensive tasks • Database operations • Network access • Incorrect algorithm • Unnecessary work - Can we cache it?
  • 5.
    Ruby benchmark • DisableGarbage Collection first with: GC.disable • Part of the Ruby standard library • I prefer to use it like this: ! time = Benchmark.realtime do # The code I’m testing end
  • 6.
    benchmark-ips • https://github.com/evanphx/benchmark-ips • Benchmarksa blocks iterations/second. • For short snippits of code, IPS automatically figures out how many times to run the code to get interesting data • Provides a standard deviation measure which is very cool
  • 7.
    stackprof • https://github.com/tmm1/stackprof • ForRuby 2.1 and above • Samples what the CPU is spending time on • https://github.com/alisnic/stackprof-webnav is a Web UI for stackprof dumps • https://github.com/quirkey/stackprof-remote is middleware and CLI for fetching and interacting with stackprof
  • 8.
    How do wefind expensive things? • Experience / Knowledge • Logs • Ruby's own benchmark module • External tools • benchmark-ips, stackprof, time • SaaS metrics tools • Librato, Datadog, Skylight, New Relic, Keen IO
  • 9.
    Reduce the impact •Optimise the application • Optimise the environment
  • 10.
    Optimise the Application •Reduce heavy I/O • ActiveRecord tweaks • Caching
  • 11.
    Reduce heavy I/O Sillyexample, but it illustrates the point. Writing to the screen is more expensive than writing to memory ! 1_000_000.times do puts "Hello world!" end ! s = String.new 1_000_000.times do s << "Hello world!n" end puts s 10.5 seconds vs. 6.5 seconds - But 2.28 times more memory
  • 12.
    Reduce heavy I/O •Same applies for database calls, event processing, etc. One large call is often much faster than many little calls. • I’ve been playing with Keen IO lately, their demo app is at: http:// keen-gem-example.herokuapp.com/ • 50 calls, 1 item each: 20094ms • 1 call with 50 items: 284ms
  • 13.
    ActiveRecord • Know yourSQL • Check logs to see what ActiveRecord is actually doing • Lots of tools to help us
  • 14.
    ActiveRecord sum example Iwant to get a total of all to the orders in the database Order.sum(&:total) Order Load (49.3ms) SELECT "orders".* FROM "orders" Order.sum(:total) (4.0ms) SELECT SUM("orders"."total") AS sum_id FROM "orders"
  • 15.
  • 16.
  • 17.
    ActiveRecord Tools • rack-mini-profiler •RailsPanel • rails-footnotes • Bullet • Peek
  • 18.
  • 19.
    RailsPanel • https://github.com/dejan/ rails_panel • ChromeExtension that provides information about the Rails request. Includes Database, rendering and total times.
  • 20.
    Memoization Memoization is acaching technique of storing a computed value to avoid duplicated work by future calls. 1. Perform some work 2. Store the result of that work 3. Use the stored data the next time you need the result of that work
  • 21.
    Memoization A common patternis using the conditional assignment operator: ||= def current_user User.find(session[:user_id]) end Instead store the result in an instance variable by using: def current_user @current_user ||= User.find(session[:user_id]) end
  • 22.
    identity_cache https://github.com/Shopify/identity_cache ActiveRecord cache formodel objects Extracted from Shopify @user = User.fetch(id) instead of @user = User.find(id) Uses an after_commit hook to expire the object from cache
  • 23.
    Rails Cache • Usethe Dalli gem as the Rails cache store def fetch(id) Rails.cache.fetch("product-#{id}", expires_in: 5.minutes) do Product.find(id) end end
  • 24.
    Is Ruby thebest tool? • Sometimes…shock, horror…Ruby might not be the best tool for the job. • Where Ruby may not be the best fit: • CPU bound • Async I/O
  • 25.
    Leibniz formula forπ 1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 … = π/4 20 million iterations CPU bound task • Ruby: 3.78 seconds • Rust: 0.238 seconds
  • 26.
    Optimise the Environment •Low hanging fruit, faster server, database, more memory • Ruby versions • Network topology • Caching systems • Web performance
  • 27.
    Web performance • Nopoint shaving 1/10th of a second off your database access time if you’ve got a 3Mb background image. • http://gtmetrix.com/ • http://tools.pingdom.com/fpt/
  • 28.
    No more lowhanging fruit • “Ruby Under a Microscope” by Pat Shaughnessy • Utilization Saturation and Errors (USE) Method
  • 29.