Advanced Performance Optimization
- f Rails Applications
Alexander Dymo RailsConf 2009
www.acunote.com
http://en.oreilly.com/rails2009/public/schedule/detail/8615
Project Management and Scrum Software
Advanced Performance Optimization of Rails Applications - - PowerPoint PPT Presentation
Project Management and Scrum Software Alexander Dymo RailsConf 2009 Advanced Performance Optimization of Rails Applications http://en.oreilly.com/rails2009/public/schedule/detail/8615 www.acunote.com What Am I Optimizing? Project Management
Alexander Dymo RailsConf 2009
www.acunote.com
http://en.oreilly.com/rails2009/public/schedule/detail/8615
Project Management and Scrum Software
2 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
What Am I Optimizing?
Online project management and Scrum software Ruby on Rails application since inception in 2006
http://en.oreilly.com/rails2009/public/schedule/detail/8615
3 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Performance Degradation Over Time
100 150 200 250 300
April 2008 May 2008 June 2008 July 2008 Request Time (on development box), %
Actually Happens: O(nc) Best Case: O(log n)
4 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Solutions?
5 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Solutions?
6 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
7 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
What To Optimize?
8 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
What To Optimize?
9 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
10 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
How To Optimize?
11 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Three Rules Of Performance Optimization
...the universal experience of programmers who have been using measurement tools has been that their intuitive guesses fail... Knuth
12 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Three Rules Of Performance Optimization
13 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Three Rules Of Performance Optimization
14 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
15 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Ruby: Date Class
What's wrong with Date?
> puts Benchmark.realtime { 1000.times { Time.mktime(2009, 5, 6, 0, 0, 0) } } 0.005 > puts Benchmark.realtime { 1000.times { Date.civil(2009, 5, 6) } } 0.080
16x slower than Time! Why?
%self total self wait child calls name 7.23 0.66 0.18 0.00 0.48 18601 <Class::Rational>#reduce 6.83 0.27 0.17 0.00 0.10 5782 <Class::Date>#jd_to_civil 6.43 0.21 0.16 0.00 0.05 31528 Rational#initialize 5.62 0.23 0.14 0.00 0.09 18601 Integer#gcd
16 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Ruby: Date Class
Fixing Date: Use C, Luke! Date::Performance gem with Date partially rewritten in C by Ryan Tomayko (with my patches in 0.4.7)
> puts Benchmark.realtime { 1000.times { Time.mktime(2009, 5, 6, 0, 0, 0) } } 0.005 > puts Benchmark.realtime { 1000.times { Date.civil(2009, 5, 6) } } 0.080 > require 'date/performance' puts Benchmark.realtime { 1000.times { Date.civil(2009, 5, 6) } } 0.006
git clone git://github.com/rtomayko/date-performance.git rake package:build cd dist && gem install date-performance-0.4.7.gem
17 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Ruby: Date Class
Real-world impact of Date::Performance: Before: 0.95 sec After: 0.65 sec 1.5x!
18 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Ruby: Misc
Use String::<< instead of String::+=
> long_string = "foo" * 100000 > Benchmark.realtime { long_string += "foo" } 0.0003 > Benchmark.realtime { long_string << "foo" } 0.000004
Avoid BigDecimal comparisons with strings and integers
> n = BigDecimal("4.5") > Benchmark.realtime { 10000.times { n <=> 4.5 } } 0.063 > Benchmark.realtime { 10000.times { n <=> BigDecimal("4.5") } } 0.014
in theory: 4.5x in practice: 1.15x in theory: 75x in practice: up to 70x
19 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
20 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Rails: Preloading Associations
Preloading associations is always a good idea... except when Rails can't do the job:
class Foo belongs_to :bar end foos = Foo.find_by_sql('select * from foos inner join bar') foos.first.bar #extra SQL query!
21 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Rails: Preloading Associations
Virtual Attributes plugin: http://github.com/acunote/virtual_attributes/
class Bar end class Foo belongs_to :bar preloadable_association :bar end foos = Foo.find_by_sql(' select * from foos left outer join (select id as preloaded_bar_id, name as preloaded_bar_name from bars) as bars
foos.first.bar #no extra SQL query!
22 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Rails: String Callbacks
What can be wrong with this code?
class Task < ActiveRecord::Base before_save "some_check()" end ... 100.times { Task.create attributes }
Kernel#binding is called to eval() the string callback That will duplicate your execution context in memory! More memory taken => More time for GC
23 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Rails: Partial Rendering
Not too uncommon, right?
<% for object in objects %> #1000 times <%= render :partial => 'object', :locals => { :object => object } %> <% end %>
We create 1000 View instances for each object here! Why?
24 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
list.rhtml
Optimizing Rails: Partial Rendering
Template inlining for the resque:
<% for object in objects %> #1000 times <%= render :partial => 'object', :locals => { :object => object }, :inline => true %> <% end %>
list.rhtml _object.rhtml _object.rhtml _object.rhtml _object.rhtml _object.rhtml _object.rhtml _object.rhtml _object.rhtml
25 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Rails: Partial Rendering
Template Inliner plugin: http://github.com/acunote/template_inliner/ Real world effect from template inlining: Rendering of 300 objects, 5 partials for each object without inlining: 0.89 sec with inlining: 0.75 sec
26 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
27 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Database
How to optimize PostgreSQL: explain analyze explain analyze explain analyze ...
28 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Database: PostgreSQL Tips
EXPLAIN ANALYZE explains everything, but... ... run it also for the "cold" database state! Example: complex query which works on 230 000 rows and does 9 subselects / joins: cold state: 28 sec, hot state: 2.42 sec Database server restart doesn't help Need to clear disk cache: sudo echo 3 | sudo tee /proc/sys/vm/drop_caches (Linux)
29 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing Database: PostgreSQL Tips
Use any(array ()) instead of in() to force subselect and avoid join
explain analyze select * from issues where id in (select issue_id from tags_issues); QUERY PLAN
Merge Cond: (issues.id = tags_issues.issue_id)
Total runtime: 605.274 ms explain analyze select * from issues where id = any( array( (select issue_id from tags_issues) ) ); QUERY PLAN
Recheck Cond: (id = ANY ($0)) InitPlan
Index Cond: (id = ANY ($0)) Total runtime: 325.205 ms
30 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Database Optimization: PostgreSQL Tips
Push down conditions into subselects and joins PostgreSQL often won't do that for you
select *, (select notes.author from notes where notes.issue_id = issues.id ) as note_authors from issues where org_id = 1 select *, (select notes.author from notes where notes.issue_id = issues.id and org_id = 1 ) as note_authors from issues where org_id = 1
Issues
id serial name varchar
integer
Notes
id serial name varchar issue_id integer
integer
31 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
What To Do?
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
32 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
33 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
= Acunote Benchmarks = MRI JRuby 1.9.1 Date/Time Intensive Ops 1.23 0.58 0.53 Rendering Intensive Ops 0.61 0.44 0.30 Calculations Intensive Ops 2.57 1.79 1.33 Database Intensive Ops 5.58 4.63 3.29
34 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
= Acunote Benchmarks = MRI JRuby 1.9.1 Date/Time Intensive Ops 1x 2.1x 2.3x Rendering Intensive Ops 1x 1.4x 2.0x Calculations Intensive Ops 1x 1.4x 1.9x Database Intensive Ops 1x 1.2x 1.7x
JRuby: 1.5x faster Ruby 1.9: 2.0x faster
35 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
36 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
This is where all the improvement went:
Acunote Copy Tasks Benchmark MRI JRuby 1.9.1 Request Time 5.52 4.45 3.24 Template Rendering Time 0.53 0.21 0.21 Database Time 0.70 1.32 0.69 GC Time 1.07 N/A 0.62
Much faster template rendering! Less GC! JDBC database driver performance issue with JRuby?
37 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
38 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
Things I usually see in the profiler after optimizing:
%self self calls name 2.73 0.05 351 Range#each-1 2.73 0.05 33822 Hash#[]= 2.19 0.04 4 Acts::AdvancedTree::Tree#walk_tree 2.19 0.04 44076 Hash#[] 1.64 0.03 1966 Array#each-1 1.64 0.03 378 Org#pricing_plan 1.64 0.03 1743 Array#each 1.09 0.02 1688 ActiveRecord::AttributeMethods#respond_to? 1.09 0.02 1311 Hash#each 1.09 0.02 6180 ActiveRecord::AttributeMethods#read_attribute_before_typecast 1.09 0.02 13725 Fixnum#== 1.09 0.02 46736 Array#[] 1.09 0.02 15631 String#to_s 1.09 0.02 24330 String#concat 1.09 0.02 916 ActiveRecord::Associations#association_instance_get 1.09 0.02 242 ActionView::Helpers::NumberHelper#number_with_precision 1.09 0.02 7417 Fixnum#to_s
39 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
# of method calls during one request: 50 000 - Array 35 000 - Hash 25 000 - String Slow classes written in Ruby: Date Rational
40 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
Alternative Rubys optimize mostly:
41 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
Alternative Rubys optimize mostly:
42 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Alternative Ruby
JRuby: if your application works with it (run requests hundreds of times to check) Ruby 1.9: if all gems you need are ported
43 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
44 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
Issues we experienced deploying on Engine Yard: 1) VPS is just too damn slow 2) VPS may have too little memory to run the request! 3) shared database server is a problem 4) network filesystem may cause harm as well
45 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
VPS may have too little memory to run the request Think 512M should be enough? Think again. We saw requests that took 1G of memory! Solutions:
46 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
You're competing for memory cache on a shared server:
47 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
You're competing for memory cache on a shared server:
48 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
As a result, your database can always be in a "cold" state and you read data from disk, not from memory! complex query which works on 230 000 rows and does 9 subselects / joins: from disk: 28 sec, from memory: 2.42 sec Solutions:
push down SQL conditions
sudo echo 3 | sudo tee /proc/sys/vm/drop_caches
49 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
fstat() is slow on network filesystem (GFS) Request to render list of tasks in Acunote:
0.50 - 2.50 sec
50 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
fstat() is slow on network filesystem (GFS) Couldn't figure out why until we ran strace We used a) filesystem store for fragment caching b) expire_fragment(regexp) Later looked through all cache directories even though we knew the cache is located in only one specific subdir
51 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimizing For Shared Environment
fstat() is slow on network filesystem (GFS) Solution: memcached instead of filesystem if filesystem is ok, here's a trick: http://blog.pluron.com/2008/07/hell-is-paved-w.html
52 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
53 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Live Debugging
To see what's wrong on "live" application: For Linux: strace and oprofile For Mac and Solaris: dtrace For Windows: uhm... about time to switch ;) To monitor for known problems: monit nagios
54 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
55 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Load Balancing
The problem of round-robin and fair load balancing
Rails App 1 Rails App 2 Rails App 3 1 1 3 2 1 3 per-process queues 2 2
56 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Load Balancing
Solution: the global queue
Rails App 1 Rails App 2 Rails App 3 2 1 4 5 3
57 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Load Balancing
Think you need mod_rails / Passenger for this? You're right, but... you can emulate this with nginx and mongrels
58 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Load Balancing
Dedicated queues for long-running requests
Rails App 1 Rails App 2 Rails App 3 1 1 2 1 3 queue for long-running requests 2 regular per-process queues
59 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Load Balancing
nginx configuration for dedicated queues
upstream mongrel { server 127.0.0.1:5000; server 127.0.0.1:5001; } upstream rss_mongrel { server 127.0.0.1:5002; } server { location / { location ~ ^/feeds/(rss|atom) { if (!-f $request_filename) { proxy_pass http://rss_mongrel; break; } } if (!-f $request_filename) { proxy_pass http://mongrel; } } }
60 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
61 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimize For The User: HTTP
Network and Frontend Backend
Things to consider:
(javascript_include_tag :all, :cache => true)
5% 95%
62 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
63 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimize Frontend: Javascript
64 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimize Frontend: Javascript
65 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimize Frontend: Javascript
Things you don't want to hear from your users:
said the user after clicking
with plain javascript (no AJAX)
66 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimize Frontend: Javascript
Known hotspots in Javascript:
67 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Things To Optimize
– Ruby code – Rails code – Database queries – Alternative Ruby
– Shared filesystems and databases – Live debugging – Load balancing
– HTTP – Javascript – Internet Explorer
68 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimize Frontend: IE
Unsurprisingly... IE is also slow! Slow things that are especially slow in IE:
69 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Optimize Frontend: IE
profiler in IE8 fast in IE => fast everywhere else!
70 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast!
71 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast!
72 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Measure
Keep a set of benchmarks for most frequent user requests. For example:
Benchmark Burndown 120 0.70 ± 0.00 Benchmark Inc. Burndown 120 0.92 ± 0.01 Benchmark Sprint 20 x (1+5) (C) 0.45 ± 0.00 Benchmark Issues 100 (C) 0.34 ± 0.00 Benchmark Prediction 120 0.56 ± 0.00 Benchmark Progress 120 0.23 ± 0.00 Benchmark Sprint 20 x (1+5) 0.93 ± 0.00 Benchmark Timeline 5x100 0.11 ± 0.00 Benchmark Signup 0.77 ± 0.00 Benchmark Export 0.20 ± 0.00 Benchmark Move Here 20/120 0.89 ± 0.00 Benchmark Order By User 0.98 ± 0.00 Benchmark Set Field (EP) 0.21 ± 0.00 Benchmark Task Create + Tag 0.23 ± 0.00 ... 30 more ...
73 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Measure
Benchmarks as a special kind of tests:
class RenderingTest < ActionController::IntegrationTest def test_sprint_rendering login_with users(:user), "user" benchmark :title => "Sprint 20 x (1+5) (C)", :route => "projects/1/sprints/3/show", :assert_template => "tasks/index" end end Benchmark Sprint 20 x (1+5) (C) 0.45 ± 0.00
74 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Measure
Benchmarks as a special kind of tests:
def benchmark(options = {}) (0..100).each do |i| GC.start pid = fork do begin
ActiveRecord::Base.transaction do elapsed_time = Benchmark::realtime do request_method = options[:post] ? :post : :get send(request_method, options[:route]) end
raise CustomTransactionError end rescue CustomTransactionError exit end end Process::waitpid pid ActiveRecord::Base.connection.reconnect! end values = File.read("values") print "#{mean(values).to_02f} ± #{sigma(values).to_02f}\n" end
75 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Query Testing
Losing 10ms in benchmark might seem OK Except that it's sometimes not because you're running one more SQL query
76 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Query Testing
def test_queries queries = track_queries do get :index end assert_equal queries, [ "Foo Load", "Bar Load", "Event Create" ] end
77 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Query Testing
module ActiveSupport class BufferedLogger attr_reader :tracked_queries def tracking=(val) @tracked_queries = [] @tracking = val end def debug_with_tracking(message) @tracked_queries << $1 if @tracking && message =~ /3[56]\;1m(.* (Load|Create| Update|Destroy)) \(/ debug_without_tracking(message) end alias_method_chain :debug, :tracking end end class ActiveSupport::TestCase def track_queries(&block) RAILS_DEFAULT_LOGGER.tracking = true yield result = RAILS_DEFAULT_LOGGER.tracked_queries RAILS_DEFAULT_LOGGER.tracking = false result end end
78 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Use Profiler
Profiler will always tell you what's wrong:
%self total self child calls name 8.39 0.54 0.23 0.31 602 Array#each_index 7.30 0.41 0.20 0.21 1227 Integer#gcd 6.20 0.49 0.17 0.32 5760 Timecell#date 5.11 0.15 0.14 0.01 1 Magick::Image#to_blob
gem install ruby-prof use "performance" tests for Rails 2.x KCachegrind to visualize the results http://kcachegrind.sourceforge.net
79 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Use Profiler
80 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Optimize CPU and Memory
Memory profiler will explain the missing details: Example benchmark: 5.52 sec request time, 1.07 sec GC time Consumed memory: 55M Ruby runs GC after allocating 8M memory or doing 10000 allocations Simple math: 55 / 8 = 6 GC calls Each GC call takes 0.18 sec!
81 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Keep It Fast: Optimize CPU and Memory
How to use memory profiler: Recompile Ruby with GC patch http://www.acunote.com/system/ruby186-p287-gc.patch Reinstall ruby-prof Use RUBY_PROF_MEASURE_MODE=memory when running ruby-prof http://blog.pluron.com/2008/02/memory-profilin.html
82 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Remember!
Measure, measure, measure... (with ruby-prof) Optimize only what's slow Optimize not only CPU, but memory Optimize for user experience Keep a set of performance regression tests Monitor performance
83 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Help Us Make Ruby and Rails Faster!
(to profile memory without recompiling Ruby)
84 / 84 Alexander Dymo • Advanced Performance Optimization of Rails Applications • RailsConf 2009
Project Management and Scrum Software
Slides for this talk: http://en.oreilly.com/rails2009/public/schedule/detail/8615 Rails performance articles and more: http://blog.pluron.com