Nick Sieger: RailsConf: Stefan Kaes - Rails Performancetag:blog.nicksieger.com,2005:TypoTypo2007-08-31T17:37:14+00:00Nickurn:uuid:34aa829a-7857-4ac1-a9ba-48bcd9fa1dec2006-08-13T17:06:51+00:002007-08-31T17:37:14+00:00Comment on RailsConf: Stefan Kaes - Rails Performance by Nick<p>Heh, yeah, maybe it’s about time for me to get off my rear end and customize my typo so it doesn’t look like every other typo blog out there, including railsexpress.de!</p>Yanurn:uuid:f3ef53b0-7342-4994-b2a6-1a0bfb5077af2006-08-11T22:33:35+00:002007-08-31T17:37:15+00:00Comment on RailsConf: Stefan Kaes - Rails Performance by Yan<p>bleh, my bad, I thought this was stefan’s blog :)</p>Yanurn:uuid:5b6659a0-bec4-4774-9df5-a03c764944c22006-08-11T22:33:04+00:002007-08-31T17:37:15+00:00Comment on RailsConf: Stefan Kaes - Rails Performance by Yan<p>Hi Stefan,</p>
<p>any news on the template optimizer? Sounds like a really interesting and valuable idea. I am very concerned with rails performance in the views..thanks!</p>Nick Siegerurn:uuid:032b3183-e579-4896-a439-2f62ee8279e62006-06-23T20:40:00+00:002007-08-31T16:41:56+00:00RailsConf: Stefan Kaes - Rails Performance<p>Stefan starts by citing a factor of 4-5 improvement in performance in
Rails over the last year.</p>
<h2>Performance, broken down</h2>
<ul>
<li>Latency – how fast</li>
<li>Throughput – how many</li>
<li>Utilization – how idle is the cpu</li>
<li>Cost efficiency – performance per unit cost</li>
</ul>
<p>For completeness calculate the min, max, mean and standard deviation
of these metrics and use the deviation as your guide for how reliable
the data is.</p>
<h2>Tools</h2>
<ul>
<li>Log files (level > <code>Logger::DEBUG</code>)</li>
<li><a href="http://rails-analyzer.rubyforge.org/">Rails Analyzer Tools</a> (Eric Hodel)</li>
<li>Benchmarker (<code>script/benchmarker</code>)</li>
<li>DB vendor tools</li>
<li>Apache bench (<code>ab</code> or <code>ab2</code>)</li>
<li>httperf</li>
<li><a href="http://rubyforge.org/projects/railsbench">railsbench</a> (Stefan Kaes)</li>
</ul>
<h2>Railsbench</h2>
<p>Railsbench measuress raw performance of rails request processing.
It’s configured using <code>config/benchmarks.yml</code> and
<code>config/benchmarks.rb</code>. These files let you control which requests
get benchmarked, whether to create a new session when benchmarking
them, etc.</p>
<h2>Profiling Tools</h2>
<ul>
<li>Ruby Profiler</li>
<li>Zen Profiler</li>
<li>rubyprof</li>
<li>Rails profiler script</li>
<li><a href="http://www.softwareverify.com/rubyPerformanceValidator/index.html">Ruby Performance Validator</a></li>
</ul>
<p>At this point Stefan gave an overview of RPV, which appears to be a
nifty tool that lets you get typical hotspot tree views of where time
is spent in code. It currently only runs on Windows.</p>
<h2>Top Rails Performance Problems</h2>
<ul>
<li>slow helper methods</li>
<li>complicated routes</li>
<li>associations – navigating and eager loading vs. proxy loading</li>
<li>retrieving too much data from the DB</li>
<li>slow session storage (e.g., ActiveRecord store)</li>
</ul>
<p>Stefan says that in his experience, DB performance is generally not a
big factor or bottleneck. Instantiating ActiveRecord objects is
expensive, though.</p>
<h2>Session containers</h2>
<ul>
<li>In memory – if you server crashes…oops. Also doesn’t scale.</li>
<li>File system – easy to set up, scales with NFS, but slower than…</li>
<li>ActiveRecordStore – easy to set up since it comes with Rails, but
much slower than…</li>
<li>SQLSessionStore – which uses the same table structure as
ActiveRecordStore, but was written by Stefan to overcome performance
issues with ActiveRecordStore. Setup is more involved.</li>
<li>memcached – slightly faster than SQLSessionStore, scales best, but
setup is also more involved.</li>
<li>DrbStore – distributed ruby store</li>
</ul>
<h2>Caching</h2>
<ul>
<li>Full pages – fastest, complete pages are served on the
filesystem. Web server bypasses appserver for rendering. If you
have private pages, you can’t use it.</li>
<li>Actions – pages are cached after an action is rendered. The
user ID can be used as part of the storage key.</li>
<li>Fragments – fragments can be cached in memory, on the file system,
in a DrbStore, or in memcached. Memcached scales the best but
doesn’t support expiring fragments by regular expression.</li>
</ul>
<h2>ActionController</h2>
<ul>
<li>Stefan recommends avoiding components, and replacing them with
helpers or partials. He has not found a use for them.</li>
</ul>
<h2>ActionView</h2>
<ul>
<li>Don’t create unnecessary instance variables in the controller;
creating them in the view with <code>instance_variable_set</code> and accessing
with <code>instance_variable_get</code> is slow.</li>
</ul>
<h2>Helpers</h2>
<ul>
<li>pluralize – don’t use the inflector if you don’t need to, it’s expensive.</li>
<li>link<em>to and url</em>for are among the slowest helpers, since they need
to use routes. Instead, if you have page with lots of links, you
might consider hard-coding the links. This reduces the amount of GC
by up to 50% and the GC time down by a few percentage points (11.3%
to 8.7% of total processing time).</li>
</ul>
<h2>ActiveRecord</h2>
<ul>
<li>use the <code>:include</code> option to prefetch associations, it avoids extra
onesy-twosy SQL statements.</li>
<li>use piggy-backing plugin for <code>has_one</code> or <code>belongs_to</code> relationships
– allows you to retrieve extra attributes from additional tables in
the same fetch query.</li>
<li>Field values are retrieved from the DB mostly as strings, so type
conversion happens on each access, which can be slow.</li>
</ul>
<h2>Language-level and miscellaneous issues</h2>
<ul>
<li>Method calls are the slowest – don’t needlessly create method
abstractions</li>
<li>Short-circuit intermediate results to improve performance</li>
<li>Cache results in instance variables or class variables</li>
<li>Don’t call <code>ObjectSpace.each_object</code> on each request</li>
</ul>
<h2>Ruby Memory Management</h2>
<ul>
<li>designed for batch scripts, not long-running servers.</li>
<li>no generational garbage collection.</li>
<li>this is suboptimal for Rails because ASTs are stored on the heap
(biggest portion of non-garbage for Rails apps), and get
processed/traversed more often than they need to be</li>
<li><a href="http://rubyforge.org/projects/railsbench">Railsbench</a> includes a patch to allow one to recompile Ruby
and tweak the garbage collector.</li>
</ul>
<h2>Rails Template Optimizer</h2>
<ul>
<li>Stefan has started a project to “compile” templates.</li>
<li>The idea is to cache results of some ERb scriptlets and essentially
“compile” or replace the template with one that has more expressions
expanded or inlined.</li>
<li>Code forthcoming; I assume you can stay tuned to <a href="http://railsexpress.de/blog/">Rails
Express</a> for news.</li>
</ul>
<h2>Questions</h2>
<ul>
<li>There was a question on JRuby – Stefan replied that it would
certainly solve GC issues, but he doesn’t know if it’s in a state to
be able to benchmark Rails requests.</li>
<li>What are your recommendations for a web server. <em>I don’t have
any.</em></li>
<li>Is horizontal or vertical scaling better? <em>I don’t know, I’ve
been focused on making single requests go fast, so I don’t have
enough experience.</em></li>
</ul>