Nick Sieger: RailsConf: Stefan Kaes - Rails Performance tag:blog.nicksieger.com,2005:Typo Typo 2007-08-31T17:37:14+00:00 Nick urn:uuid:34aa829a-7857-4ac1-a9ba-48bcd9fa1dec 2006-08-13T17:06:51+00:00 2007-08-31T17:37:14+00:00 Comment on RailsConf: Stefan Kaes - Rails Performance by Nick <p>Heh, yeah, maybe it&#8217;s about time for me to get off my rear end and customize my typo so it doesn&#8217;t look like every other typo blog out there, including railsexpress.de!</p> Yan urn:uuid:f3ef53b0-7342-4994-b2a6-1a0bfb5077af 2006-08-11T22:33:35+00:00 2007-08-31T17:37:15+00:00 Comment on RailsConf: Stefan Kaes - Rails Performance by Yan <p>bleh, my bad, I thought this was stefan&#8217;s blog :)</p> Yan urn:uuid:5b6659a0-bec4-4774-9df5-a03c764944c2 2006-08-11T22:33:04+00:00 2007-08-31T17:37:15+00:00 Comment 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 Sieger urn:uuid:032b3183-e579-4896-a439-2f62ee8279e6 2006-06-23T20:40:00+00:00 2007-08-31T16:41:56+00:00 RailsConf: 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 &#8211; how fast</li> <li>Throughput &#8211; how many</li> <li>Utilization &#8211; how idle is the cpu</li> <li>Cost efficiency &#8211; 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&#8217;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 &#8211; 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 &#8211; if you server crashes&#8230;oops. Also doesn&#8217;t scale.</li> <li>File system &#8211; easy to set up, scales with NFS, but slower than&#8230;</li> <li>ActiveRecordStore &#8211; easy to set up since it comes with Rails, but much slower than&#8230;</li> <li>SQLSessionStore &#8211; 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 &#8211; slightly faster than SQLSessionStore, scales best, but setup is also more involved.</li> <li>DrbStore &#8211; distributed ruby store</li> </ul> <h2>Caching</h2> <ul> <li>Full pages &#8211; fastest, complete pages are served on the filesystem. Web server bypasses appserver for rendering. If you have private pages, you can&#8217;t use it.</li> <li>Actions &#8211; pages are cached after an action is rendered. The user ID can be used as part of the storage key.</li> <li>Fragments &#8211; fragments can be cached in memory, on the file system, in a DrbStore, or in memcached. Memcached scales the best but doesn&#8217;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&#8217;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 &#8211; don&#8217;t use the inflector if you don&#8217;t need to, it&#8217;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 &#8211; 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 &#8211; don&#8217;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&#8217;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 &#8220;compile&#8221; templates.</li> <li>The idea is to cache results of some ERb scriptlets and essentially &#8220;compile&#8221; 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 &#8211; Stefan replied that it would certainly solve GC issues, but he doesn&#8217;t know if it&#8217;s in a state to be able to benchmark Rails requests.</li> <li>What are your recommendations for a web server. <em>I don&#8217;t have any.</em></li> <li>Is horizontal or vertical scaling better? <em>I don&#8217;t know, I&#8217;ve been focused on making single requests go fast, so I don&#8217;t have enough experience.</em></li> </ul>