<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:thr="http://purl.org/syndication/thread/1.0">
    <title>Nimble Method</title>
    <link rel="self" type="application/atom+xml" href="http://blog.pluron.com/atom.xml" />
    <link rel="hub" href="http://hubbub.api.typepad.com/" />
    <link rel="alternate" type="text/html" href="http://blog.pluron.com/" />
    <id>tag:typepad.com,2003:weblog-497997</id>
    <updated>2009-05-01T14:14:31-07:00</updated>
    
    <generator uri="http://www.typepad.com/">TypePad</generator>
    <entry>
        <title>The future of Rails is Ruby 1.9 - real performance of 1.8, JRuby and 1.9 compared</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2009/05/ruby-19-performance.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2009/05/ruby-19-performance.html" thr:count="17" thr:updated="2009-05-22T06:36:44-07:00" />
        <id>tag:typepad.com,2003:post-66253667</id>
        <published>2009-05-01T14:14:31-07:00</published>
        <updated>2009-05-01T14:46:29-07:00</updated>
        <summary>This is the second post of my pre-RailsConf series of blog posts on Rails application performance optimization. I&#39;ll be [presenting at RailsConf about this and many other aspects of performance optimization](http://blog.pluron.com/2009/04/performance-freaks-live-at-railsconf-2009.html) on May 6th, so you&#39;re welcome to join me...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        <category scheme="http://www.sixapart.com/ns/types#category" term="Rails Performance" />
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>This is the second post of my pre-RailsConf series of blog posts on Rails application performance optimization. I'll be <a href="http://blog.pluron.com/2009/04/performance-freaks-live-at-railsconf-2009.html">presenting at RailsConf about this and many other aspects of performance optimization</a> on May 6th, so you're welcome to join me at RailsConf and please watch for more performance-related articles in this blog.</p>

<p><small>See also the previous article in this series on <a href="http://blog.pluron.com/2009/04/ruby-date-class.html">improving Date class performance</a>.</small>
<hr/></p>

<p>We saw <a href="http://antoniocangiano.com/2009/03/29/why-macruby-matters/">lots of</a> <a href="http://antoniocangiano.com/2007/12/03/the-great-ruby-shootout/">synthetic benchmarks</a> proving that alternative ruby implementations (namely the only two finished ones - JRuby and Ruby 1.9) are faster than good old 1.8 aka MRI. But nobody so far ported the production application to prove they are faster in the real world conditions. We did.<a style="float: right;" href="http://pluron.typepad.com/.a/6a00d83451c8d369e2011570645f83970b-pi"><img class="at-xid-6a00d83451c8d369e2011570645f83970b" alt="Turtle" src="http://pluron.typepad.com/.a/6a00d83451c8d369e2011570645f83970b-320wi" style="margin: 10px 0px 5px 5px;" /></a></p>

<p>We ported Acunote - our online enterprise project management and <a href="http://www.acunote.com">Scrum software</a> to both JRuby and Ruby 1.9 and ran our set of performance benchmarks. We have 30 benchmarks that check the server response time for most typical user requests. We use those benchmarks to check for performance regressions, and this time we used them to check the Acunote's performance with alternative rubys.</p>

<div style="float:right; width:300px; font-size:16px; font-weight:bold; font-style:italic; margin-left:28px">
&quot;Ruby 1.9 is 2x faster than 1.8. But the real world improvements are even better.&quot;
</div>

<p>There're four common request types for Acunote, ones that primarily involve date/time operations, rendering, numerical calculations and ActiveRecord/database operations.</p>

<p>Here are the timings for four requests, one for each type:</p>

<table width="100%" cellpadding="5px" border="1px" style="border-collapse: collapse">
<thead>
<tr>
  <th rowspan="2">Acunote Benchmark</th>
  <th colspan="3">Request Time, sec</th>
  <th colspan="2">Improvement</th>
</tr>
<tr>
  <th width="15%">1.8.6</th>
  <th width="15%">JRuby 1.2.0</th>
  <th width="15%">1.9.1</th>
  <th width="10%">JRuby</th>
  <th width="10%">1.9.1</th>
</tr>
</thead>
<tbody>
<tr>
  <td>Date/Time Intensive</td>
  <td>1.23</td>
  <td>0.58</td>
  <td>0.53</td>
  <td>2.1x</td>
  <td><b>2.3x</b></td>
</tr>
<tr>
  <td>Rendering Intensive</td>
  <td>0.61</td>
  <td>0.44</td>
  <td>0.30</td>
  <td>1.4x</td>
  <td><b>2.0x</b></td>
</tr>
<tr>
  <td>Calculations Intensive</td>
  <td>2.57</td>
  <td>1.79</td>
  <td>1.33</td>
  <td>1.4x</td>
  <td><b>1.9x</b></td>
</tr>
<tr>
  <td>Database Intensive</td>
  <td>5.58</td>
  <td>4.63</td>
  <td>3.29</td>
  <td>1.2x</td>
  <td><b>1.7x</b></td>
</tr>
</tbody>
</table>

<p>Here are those timings expanded with rendering and database time:</p>

<table width="100%" cellpadding="5px" border="1px" style="border-collapse: collapse">
<thead>
<tr>
  <th rowspan="2">Acunote Benchmark</th>
  <th colspan="3">Request Time / Rendering Time / DB Time, msec</th>
</tr>
<tr>
  <th>1.8.6</th>
  <th>JRuby 1.2.0</th>
  <th>1.9.1</th>
</tr>
</thead>
<tbody>
<tr>
  <td>Date/Time Intensive</td>
  <td>1230 / 0 / 65</td>
  <td>580 / 1 / 45</td>
  <td>530 / 1 / 37</td>
</tr>
<tr>
  <td>Rendering Intensive</td>
  <td>610 / 328 / 33</td>
  <td>440 / 273 / 45</td>
  <td>300 / 197 / 37</td>
</tr>
<tr>
  <td>Calculations Intensive</td>
  <td>2570 / 380 / 38</td>
  <td>1790 / 215 / 54</td>
  <td>1330 / 204 / 39</td>
</tr>
<tr>
  <td>Database Intensive</td>
  <td>5580 / 529 / 700</td>
  <td>4630 / 209 / 1321</td>
  <td>3290 / 221 / 685</td>
</tr>
</tbody>
</table>

<p>As you can see from the table, Ruby 1.9 is 2x faster than 1.8. But the real world improvements are even better. Ruby 1.9 eliminates performance bottlenecks that were present in 1.8, e.g. Date class, numerical and string computations, template rendering. It lets us write more complex systems in Ruby than were ever possible before. In the immortal words of Avi Bryant, it lets us add a whole 'nother level of <a href="http://blog.caboo.se/articles/2007/4/8/heresy-and-turtles-all-the-way-down-with-avi-bryant">turtles</a>.</p>

<p>The performance improvement from JRuby is not as substantial, but it's still 1.5x faster. One of the reasons for JRuby being slower than 1.9 is PostgreSQL JDBC driver and not-working "fast" mode.</p>

<p>Want to know why 1.9 and JRuby are so much faster? Are there any other improvements and regressions with alternative Ruby's? What happened to the garbage collection performance? To learn the answers, join me at <a href="http://blog.pluron.com/2009/04/performance-freaks-live-at-railsconf-2009.html">my RailsConf session</a> or watch for the more detailed analysis in this blog.</p>
</div>
</content>



    </entry>
    <entry>
        <title>Ruby Date Class Slows You Down? Rewrite It In C!</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2009/04/ruby-date-class.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2009/04/ruby-date-class.html" thr:count="2" thr:updated="2009-05-06T10:43:01-07:00" />
        <id>tag:typepad.com,2003:post-52716414</id>
        <published>2009-04-23T00:15:08-07:00</published>
        <updated>2009-04-23T00:15:08-07:00</updated>
        <summary>This is the first post of my pre-RailsConf series of blog posts on Rails application performance optimization. I&#39;ll be [presenting at RailsConf about this and many other aspects of performance optimization](http://blog.pluron.com/2009/04/performance-freaks-live-at-railsconf-2009.html) on May 5th, so you&#39;re welcome to join me...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>This is the first post of my pre-RailsConf series of blog posts on Rails application performance optimization. I'll be <a href="http://blog.pluron.com/2009/04/performance-freaks-live-at-railsconf-2009.html">presenting at RailsConf about this and many other aspects of performance optimization</a> on May 5th, so you're welcome to join me at RailsConf and please watch for more performance-related articles in this blog.</p>

<hr/>

<p>Imagine a situation when your Ruby code is slow. Not so hard to imagine, isn't it? Now imagine that your ran a profiler (like we <a href="http://blog.pluron.com/2008/02/memory-profilin.html">did</a>) and figured out that you can do nothing to optimize. </p>

<p>Hard to imagine, you say? Not when you heavily use Ruby Date class. So what to do when there's nothing to optimize in Ruby? Simple answer - use C! Here is the story how we optimized our application by using Date class partially rewritten in C.</p>

<h2>Why Date Class Is Slow in Ruby</h2>

<p>Ruby Date implementation is an amazing piece of code handling probably any possible date usecase. But that flexibility comes not without a cost - the performance of Date class is very poor. It takes a long time to create Date objects, print them and compare them.</p>

<p>This simple benchmark should give you an idea of how slow Date is:</p>

<pre><code>&gt; puts Benchmark.realtime { 1000.times { Date.civil(2008, 7, 15) } }
0.080
&gt; puts Benchmark.realtime { 1000.times { Time.mktime(2008, 7, 15, 0, 0, 0) } }
0.005
</code></pre>

<p>Wtf you say? Contructing Date's is 16x slower than Time's? Yes it is. To understand the reason we need to dig into Date class internals.</p>

<p>When you create a Date from "civil" Gregorian year, month and day, the constructor converts those into the <a href="http://en.wikipedia.org/wiki/Julian_day">Astronomical Julian Day number</a> which is a number of days since midday January 1st, 4713BC UTC:</p>

<pre><code>&gt; d = Date.today
&gt; d.to_s
"2008-07-15"
&gt; d.ajd
Rational(4909325, 2)      #this is a fractional astronomical Julian day number
&gt; d.ajd.to_f
2454662.5                 #2454662 days and a half passed since the initial epoch
</code></pre>

<p>As you can see, any civil date is assumed to be the "start" of a day hence it always have half of the day subtracted from it. Note also that Ruby implementation uses Rational class to store the fraction without losing precision.</p>

<p>It's extremely easy and flexible to work on dates as on numbers but to obtain those numbers you have to do conversion which is roughly 20 arithmetic operations and 3 round-downs. This conversion takes place every time you construct or print the date. In addition to that, the constructor has to create an instance of Rational class. Just because Ruby interpreter is genuinely slow, all those operations take time... too much time and you can do absolutely nothing to optimize them!</p>

<h2>The Impact</h2>

<p>Now we know that Date is slow, but will it hurt the real world application? The answer really depends on how much you use dates. If you store large amounts of dates in the database, be prepared for the worst.</p>

<p>As usual my example will be <a href="http://www.acunote.com">Acunote</a>, our online project management application written in Rails. One of the things Acunote does is it keeps track of people's progress. To do that it records all changes in estimated and remaining times for each task in the system thus storing a lot of &lt;date, number&gt; tuples.</p>

<p>Using the historical data, Acunote draws <a href="http://www.acunote.com/tour/burndown">burndown graphs</a> where horizontal axis represents dates and vertical - total work remainings for each date. Obviously, to draw such graphs we need to fetch data from database, perform simple additions and output it. Sounds simple but works slow.</p>

<p>Here's what profiler says for the burndown drawing request:</p>

<pre><code>Total: 2.490000
%self     total     self     wait    child    calls  name
 7.23      0.66     0.18     0.00     0.48    18601  &lt;Class::Rational&gt;#reduce
 6.83      0.27     0.17     0.00     0.10     5782  &lt;Class::Date&gt;#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
 4.82      0.12     0.12     0.00     0.00        1  Magick::Image#to_blob
 ...
</code></pre>

<p>The top 4 method calls have nothing to do with burndown drawing. As it turns out they are called from <b>Date#civil</b> and <b>Date#&lt;=></b> methods:</p>

<pre><code>Total: 2.490000
total
 1.56  &lt;Class::Date&gt;#civil
 0.11  Date#&lt;=&gt;
 ...
</code></pre>

<p>Great! We spend <b>65%</b> of the request time just <b>constructing and comparing dates</b>. How to optimize? We had no idea because it's quite hard to optimize 20 additions and multiplications that take place inside Date class without rewriting the whole thing. The only workable solution was to rewrite slow parts in Date in C and we had been dreaming about that until we found that <a href="http://tomayko.com">Ryan Tomayko</a> already did exactly that.</p>

<h2>Date::Performance To The Rescue</h2>

<p><a href="http://tomayko.com/src/date-performance/">Date::Performance</a> library implements several core methods of Date class in C replacing the slow Ruby ones. What's important, Date constructor and conversion methods were rewritten and are now 20 times faster!</p>

<pre><code>&gt; puts Benchmark.realtime { 1000.times { Date.civil(2008, 7, 15) } }
0.004
&gt; puts Benchmark.realtime { 1000.times { Time.mktime(2008, 7, 15, 0, 0, 0) } }
0.005
</code></pre>

<p>In Acunote the burndown operation immediately became 2.5x faster after we used Date::Performance and all Date/Rational related records were absolutely absent from the profiler output!</p>

<pre><code>Total: 0.950000
%self     total     self     wait    child    calls  name
12.63      0.12     0.12     0.00     0.00        1  Magick::Image#to_blob
 8.42      0.17     0.08     0.00     0.09      602  Array#each_index
 5.26      0.07     0.05     0.00     0.02     6992  Class#new!
 5.26      0.09     0.05     0.00     0.04    42683  Kernel#===
 5.26      0.05     0.05     0.00     0.00        2  Magick::GradientFill#fill
</code></pre>

<p>Date::Performance is designed as an addition to the original Date class. You just install <b>date-performance</b> from source from the <a href="http://github.com/rtomayko/date-performance/tree">GitHub project page</a> and <b>require 'date/performance'</b> in your code.</p>

<p>Date::Performance versions 0.4.6 and earlier didn't overwrite Date::&lt;=> method (which was also reported by the profiler as a slowdown) so I went on and implemented it in C as well. The implementation is in Date::Performance 0.4.7 or on our company <a href="http://github.com/pluron/date-performance/tree">GitHub date-performance fork</a>. This gives additional 10% improvement.</p>

<p>There's one important thing about Date::Performance - make sure to run tests for your code and check that it works. <strong>Date::Performance is not a drop-in replacement</strong>. Known regressions are:</p>

<ul>
<li>month argument to the constructor can not be negative;</li>
<li>day argument to the constructor can not be negative in date-performance versions &lt; 0.4.6 (0.4.6 has my <a href="http://github.com/rtomayko/date-performance/commit/8203c325cf47516b275daab0a6770005f1248a05">patch</a> applied already);</li>
<li>Date cannot be compared with Float in 0.4.7 and later</li>
</ul>

<p>There might be other regressions I don't know about yet.</p>

<h2>Conclusions</h2>

<p>By using Date class partially reimplemented in C you can <b>speedup your application 2.5x</b>! To do that:</p>

<ul>
<li>install <a href="http://tomayko.com/src/date-performance/">Date::Performance library</a> from <a href="http://github.com/rtomayko/date-performance/tree">GitHub</a>;</li>
<li><em>require 'date/performance'</em> in your code;</li>
<li>run the tests and if everything works - enjoy!</li>
</ul>

<p>PS: many thanks to Ryan Tomayko for his rocket-fast Date implementation.</p>
</div>
</content>



    </entry>
    <entry>
        <title>Advanced Performance Optimization of Rails Applications Presentation at RailsConf 2009</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2009/04/performance-freaks-live-at-railsconf-2009.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2009/04/performance-freaks-live-at-railsconf-2009.html" thr:count="1" thr:updated="2009-04-28T16:46:22-07:00" />
        <id>tag:typepad.com,2003:post-65223521</id>
        <published>2009-04-08T11:00:44-07:00</published>
        <updated>2009-04-13T00:52:53-07:00</updated>
        <summary>Things have never been so exciting! We come to RailsConf again and this time not to spectate, but to speak! I will be giving a talk on advanced performance optimization of Rails applications on May 5th at 14:50 PDT in...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        <category scheme="http://www.sixapart.com/ns/types#category" term="Ruby on Rails" />
        
        
<content type="html" xml:lang="en-US" xml:base="http://blog.pluron.com/">
&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://www.railsconf.com/&quot;&gt;&lt;img  src=&quot;http://assets.en.oreilly.com/1/event/24/rails2009_banner_speaking_210x60.jpg&quot; alt=&quot;RailsConf 2009&quot; title=&quot;RailsConf 2009&quot; border=&quot;0&quot; height=&quot;60&quot; width=&quot;210&quot;&gt;
&lt;/a&gt;&lt;br&gt;
Things have never been so exciting! We come to RailsConf again and this time not to spectate, but to speak!&amp;nbsp;&lt;/p&gt;&lt;p&gt;I will be giving a talk on &lt;a href=&quot;http://en.oreilly.com/rails2009/public/schedule/detail/8615&quot;&gt;advanced performance optimization of Rails applications&lt;/a&gt; on May 5th at 14:50 PDT in Pavilion 9-10, Las Vegas Hilton, Las Vegas, NV.&amp;nbsp;&lt;/p&gt;&lt;p&gt;When I started working with Rails, little did I know that optimization doesn&#39;t end with speeding up my Ruby code (or Rails itself). The performance of production application heavily depends on its environment - server filesystems, databases, load balancing solutions. The browser plays the crucial role as well.&amp;nbsp;&lt;/p&gt;&lt;p&gt;In my talk I hope to not just share my knowledge about different aspects of performance optimizations, but put together all these pieces of the performance puzzle. I&#39;ll show with numbers what&#39;s important and what&#39;s not and share with you the performance-oriented practices and processes we developed in Pluron for the last 3 years.&lt;/p&gt;&lt;p&gt;
Let me outline some examples to give you a taste of what I&#39;ll be talking about. It turned out that:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;when deploying on shared databases you need to optimize for cold database state in order to have 14x speedups for your SQL queries!&lt;/li&gt;
&lt;li&gt;using Date class with some of its functions rewritten in C can give you 2.5x speedup for free!&lt;/li&gt;
&lt;li&gt;inaccurate usage of $() and $$() functions on large pages will make your application in Internet Explorer 13x slower!&lt;/li&gt;
&lt;li&gt;proper load balancing will make your application perfectly usable without any optimizations in code at all!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;

Already curious? Join me at RailsConf and you&#39;ll learn even more. If you haven&#39;t registered yet, by all means do so &lt;a href=&quot;http://en.oreilly.com/rails2009/public/register&quot;&gt;here&lt;/a&gt; and don&#39;t forget the 15% discount code &lt;em&gt;RC09FOS&lt;/em&gt; I have for you. &lt;/p&gt;&lt;p&gt;
If you&#39;re interested in the talk, please leave a comment and let me know what you think of the topics I&#39;m going to discuss. I&#39;d love to hear what I&#39;m missing, what you think is boring and what is particularly interesting for you. &lt;/p&gt;&lt;p&gt;Please also watch for updates in this blog. I&#39;ll be sharing slides here after the conference ends and I plan to write more detailed posts on each performance optimization topic. &lt;/p&gt;&lt;p&gt;
See you at RailsConf! &lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;br&gt;&lt;/p&gt;&lt;hr&gt;&lt;p&gt;PS: here&#39;s my presentation abstract for your reading pleasure:&lt;/p&gt;&lt;h3&gt;Advanced Performance Optimization of Rails Applications&lt;/h3&gt;&lt;p&gt;Alexander Dymo (Pluron, Inc.), 2:50pm Wednesday, 05/06/2009, Location: Pavilion 9 - 10&lt;/p&gt;&lt;p&gt;In this session Alexander covers steps of performance improvement process necessary to make real-world production Rails application fast and keep it so.&lt;/p&gt;&lt;p&gt;To start, your application is slow. Make it faster: &lt;/p&gt;&lt;ul&gt;
&lt;li&gt;optimizing common hotspots in Ruby code, including Date class, String::+=, BigDecimal comparisons and more &lt;/li&gt;
&lt;li&gt;preloading has_many/belongs_to associations when Rails eager loading can’t (custom joins and selects) &lt;/li&gt;
&lt;li&gt;optimizing template rendering, especially when including multiple small partials &lt;/li&gt;
&lt;li&gt;tips for faster PostgreSQL queries: manually pushing down conditions into subqueries, using arrays instead of joins and more &lt;/li&gt;
&lt;li&gt;performance characteristics of JRuby and Ruby 1.9 &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Two new plugins to make it faster faster: &lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Virtual Attributes AR plugin &lt;/li&gt;
&lt;li&gt;Template Inliner plugin &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your application is fast on development boxes, but is still slow in production. Some things to look into:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;impact of filesystem performance &lt;/li&gt;
&lt;li&gt;shared database performance under severe memory restrictions &lt;/li&gt;
&lt;li&gt;debugging live production applications (strace, dtrace, oprofile) &lt;/li&gt;
&lt;li&gt;debugging performance problems caused by load-balancing solutions commonly used with Rails &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Your application is fast on the server, but is still slow for the users. Optimizing in-browser performance: &lt;/p&gt;&lt;ul&gt;
&lt;li&gt;HTTP optimization basics, managing browser cache &lt;/li&gt;
&lt;li&gt;optimizing JavaScript performance, efficient drag-and-drop &lt;/li&gt;
&lt;li&gt;IE-specific optimizations

Your application is fast. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are the best practices and processes to keep it fast: &lt;/p&gt;&lt;ul&gt;
&lt;li&gt;performance-aware coding and unit testing &lt;/li&gt;
&lt;li&gt;continuous performance integration testing &lt;/li&gt;
&lt;li&gt;memory profiling &lt;/li&gt;
&lt;li&gt;techniques for realistic database profiling &lt;/li&gt;
&lt;li&gt;production performance monitoring &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Need to justify performance work to your boss? Alexander will use the data he gathered from production Rails application to demonstrate how quickly the performance degrades when you don’t care about it. You will understand that the continuous work on performance should be the integral part of your development and deployment process.&lt;/p&gt;&lt;/div&gt;
</content>



    </entry>
    <entry>
        <title>Why You Should Use &#39;Cache-Control: public&#39; for Caching to Work with SSL</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2008/07/why-you-should.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2008/07/why-you-should.html" thr:count="7" thr:updated="2010-04-10T10:25:34-07:00" />
        <id>tag:typepad.com,2003:post-52667646</id>
        <published>2008-07-14T04:42:40-07:00</published>
        <updated>2008-07-14T04:42:40-07:00</updated>
        <summary>We&#39;ve been using long Expires and Cache-Control: max-age headers to mark our javascripts and css as browser-cacheable for quite some time. This way the browser only does full GETs on these once, and avoids conditional GETs except on page refresh....</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        <category scheme="http://www.sixapart.com/ns/types#category" term="Rails Performance" />
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>We've been using long <i>Expires</i> and <i>Cache-Control: max-age</i>
headers to mark our javascripts and css as browser-cacheable for quite
some time.  This way the browser only does full GETs on these once,
and avoids conditional GETs except on page refresh.  This only
requires a few lines of configuration in Rails (asset timestamps and
asset packaging) and nginx and generally works well.</p>

<p>Except when it does not.  We've had some bug reports that in some
cases under SSL these files were not cached at all.  Meaning, just
going to a different page in our application would make browser do an
unconditional GET to refetch css and javascript.  Naturally, this
would hurt the performance.</p>

<p>Now, SSL in itself makes things worse.  Firefox only caches SSL
content in memory not on disk, meaning if the user closes the browser
the cache goes away.  But the reported behavior was worse than that --
no caching at all.</p>

<p>It took us a while, but we did manage to chase it down.  It seems that
Firefox divides SSL content into page marked with <i>Cache-Control:
public</i>, which have priority for caching, and the rest.  When you
open enough tabs, say 50+, with enough rich pages in Firefox, its
in-memory cache fills up and refuses to cache non-public SSL content.
We haven't had time to fully chase it down in the FF codebase, but
what's likely to happen is that FF actually does put in into cache,
then realizes that cache is full, looks for pages to evict, and right
away evicts the newly added content.  Since SSL content is not cached
on hard disk, once the memory cache is full, the non-pubic SSL files
are not cached at all. (Note that's just a hypothesis, we'd be most
curious to know what actually happens and why.)</p>

<p>The server-side workaround is to add <i>Cache-Control: public</i> to
all your SSL content you want cached.  We do:</p>

<pre><code># nginx configuration for static content  
location /static {
    expires 1y;
    add_header Cache-Control public;
}
</code></pre>

<p>This bug is present in Firefox 2 and 3, but as a user you can
configure them to work around the problem by <a href="http://kb.mozillazine.org/Browser.cache.memory.capacity">setting
<em>browser.cache.memory.capacity</em></a>
to increase the size of memory cache or using
<a href="http://kb.mozillazine.org/Browser.cache.disk_cache_ssl"><em>browser.cache.disk_cache_ ssl</em></a>
to enable disk cache for SSL pages. The default 24M memory cache size
for systems with 2G of RAM is really small, especially with FF3 being
able to handle hundreds of tabs well.  Note that the disk_cache
option works only on Firefox 3 because of
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=345181">some</a>
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=262116">bugs</a> in earlier
browser versions.</p>

<p>Small remark for <a href="http://www.acunote.com">Acunote</a> users - you don't
have to do anything about this. We enabled <i>Cache-Control:
public</i> and your Firefox will cache javascript and css files no
matter how many tabs you have.</p>
</div>
</content>



    </entry>
    <entry>
        <title>The Road to Hell Is Paved With Caching</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2008/07/hell-is-paved-w.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2008/07/hell-is-paved-w.html" thr:count="1" thr:updated="2008-07-02T15:19:52-07:00" />
        <id>tag:typepad.com,2003:post-52117060</id>
        <published>2008-07-02T10:32:39-07:00</published>
        <updated>2008-07-02T10:32:39-07:00</updated>
        <summary>In [Acunote](http://www.acunote.com) we are heavily using fragment caching. We cache whenever we can and whatever we can. Sounds like a great idea to do? Well, almost... Sometimes caching hurts and here&#39;s why. ## Prehistory ## Acunote is a project management...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        
        
<content type="html" xml:lang="en-US" xml:base="http://blog.pluron.com/">
&lt;div xmlns=&quot;http://www.w3.org/1999/xhtml&quot;&gt;&lt;p&gt;In &lt;a href=&quot;http://www.acunote.com&quot;&gt;Acunote&lt;/a&gt; we are heavily using fragment caching. We cache whenever we can and whatever we can. Sounds like a great idea to do? Well, almost... Sometimes caching hurts and here&#39;s why.&lt;/p&gt;

&lt;h2&gt;Prehistory&lt;/h2&gt;

&lt;p&gt;Acunote is a project management tool and therefore the most prominent page in Acunote is the task list in the current sprint. The page itself shows some information specific to the user who logged in and some information that is shared among the users within the user&#39;s organization.&lt;/p&gt;

&lt;p&gt;We can cache the shared part of the page in the fragment cache taking date and page filters into account. The code roughly looks like this:&lt;/p&gt;

&lt;pre&gt;
&lt;%- cache(:part =&gt; &quot;#{filters}_#{DateUtils::today}&quot;) do -%&gt;
    &lt;%= render :partial =&gt; &#39;...&#39; -%&gt;
&lt;%- end -%&gt;
&lt;/pre&gt;

&lt;p&gt;Using filesystem caching store we end up having this (simplified) cache directory layout:&lt;/p&gt;

&lt;pre&gt;
 - organization1.acunote.com
   - sprints
     - show.part=filter1.date.cache
     - show.part=filter2.date.cache
     - show.part=filter3.date.cache
 - organization2.acunote.com
   - sprints
     - show.part=filter1.date.cache
     - show.part=filter2.date.cache
     - show.part=filter3.date.cache
&lt;/pre&gt;

&lt;h2&gt;Problem&lt;/h2&gt;

&lt;p&gt;Problems start when we try to expire caches. We can&#39;t expire by url or by hash because we want to expire all cache parts (for all possible filters and for all dates). Therefore we use regexp:&lt;/p&gt;

&lt;pre&gt;
expire_fragment(%r{show.part=.*})
&lt;/pre&gt;

&lt;p&gt;&lt;strong&gt;That turned into a problem in production&lt;/strong&gt;. The reason is that expire_fragment in Rails &lt;strong&gt;searches the whole cache directory recursively&lt;/strong&gt; and removes files with names matching the regular expression. On machines with local disks that&#39;s usually fast, but on network filesystems or on shared disk cluster filesystems (like GFS in our case) stat&#39;ing lots of files can be &lt;strong&gt;really&lt;/strong&gt; slow. To make things worse, the performance of stat call on GFS really depends on the phase of the moon or something like that ;)&lt;/p&gt;

&lt;p&gt;Our logs showed that operations that would usually take less than a second took on occasion took 70 seconds! They were so slow so I could actually log in to our production servers, find the process taking too much time to complete and attach strace to it. Strace blessed me, immediately showing multiple stat&#39;s for files in the cache directory.&lt;/p&gt;

&lt;h2&gt;Solution&lt;/h2&gt;

&lt;p&gt;Longer term we&#39;ll switch to a more scalable fragment caching strategy.  There is a good reason why high-performance caches do not support fancy invalidation, and there are known way to code to their more restricted interface.  Meanwhile, immediate solution was really simple. We only had to expire fragments in specific directories and to make that easier, I wrote a little extension to Rails&#39; fragment caching code:&lt;/p&gt;

&lt;pre&gt;
module ActionController
  module Caching
    module Fragments

    #dir is the cache path relative to the cache root
    def expire_matched_fragment_in_dir(dir, regexp, options = nil)
      return unless perform_caching
      self.class.benchmark(&quot;Expired fragments in dir matching: #{regexp.source}&quot;) do
        fragment_cache_store.delete_matched_in_dir(dir, regexp, options)
      end
    end

    class UnthreadedFileStore

      def delete_matched_in_dir(dir, matcher, options = nil)
        path = @cache_path + dir
        return unless File.exist?(path) #it&#39;s ok to not have the cache dir
        search_dir(path) do |f|
          if f =~ matcher
          begin
            File.delete(f)
          rescue SystemCallError =&gt; e
            # If there&#39;s no cache, then there&#39;s nothing to complain about
          end
          end
         end
        end
      end

    end

  end
end

#include only if you use cache test plugin
module Cosinux
  module FragmentCacheTest
    class TestStore
      def delete_matched_in_dir(dir, matcher, options = nil)
        @deleted_matchers.push(matcher)
      end
    end
  end
end
&lt;/pre&gt;

&lt;h2&gt;Conclusions&lt;/h2&gt;

&lt;p&gt;Monitoring the performance of production application is sometimes even more important than profiling it on the development machines. We already had monitoring system in place so we could detect when our application got slower, figure out the root cause and and fix the problem.&lt;/p&gt;

&lt;p&gt;Another thing I highly recommend is of course &lt;b&gt;strace&lt;/b&gt;. You may indeed see that your application is doing something so weird you&#39;d never think of just looking at the code or at the profiler. So, I now declare strace to be officially added into my own arsenal of invaluable profiling tools.&lt;/p&gt;
&lt;/div&gt;
</content>



    </entry>
    <entry>
        <title>Performance Freak&#39;s View on RailsConf 2008</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2008/06/performance-fre.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2008/06/performance-fre.html" thr:count="5" thr:updated="2009-05-08T13:01:43-07:00" />
        <id>tag:typepad.com,2003:post-50793570</id>
        <published>2008-06-26T04:01:39-07:00</published>
        <updated>2008-06-26T04:01:39-07:00</updated>
        <summary>RailsConf is over and it&#39;s about time for reflections now. Two hot topics this year dominated the conference - we&#39;ve got a strong presence from people working on alternative Ruby implementations and lots of presentations on scaling and performance. In...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        <category scheme="http://www.sixapart.com/ns/types#category" term="Ruby on Rails" />
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>RailsConf is over and it's about time for reflections now. Two hot topics this year dominated the conference - we've got a strong presence from people working on alternative Ruby implementations and lots of presentations on scaling and performance. In short, my two conclusion on those topics are:</p>

<ul>
<li>one and only one MRI alternative now really works</li>
<li>Rails community focuses on scaling rather than performance</li>
</ul>

<p>Read on and I'll tell you what works and what to do if you want faster and not larger Rails.</p>

<h2>Alternative Ruby</h2>

<p>The current situation with alternative ruby implementations reminds me of the lions pride. The lion king (read MRI) is old and weak, but it's still strong enough to outperform the subadults. Despite that, some of the youngsters look promising, some them look cool and some look as undecidable. My personal opinion here is that promising implementations take existing VM and implement Ruby on top of them, cool implementation do all on their own and undecidable ones are not open source. Ok, enough generalizations, this topic deserves somebody to talk in details, so here are my stories on JRuby, Rubinius, Ruby 1.9, Iron Ruby and MagLev. </p>

<h3>JRuby Story</h3>

<p>I've been watching JRuby since its inception. I remember listening to <a href="http://headius.blogspot.com">Charles Nutter</a> back in 2006 at RailsConf Europe when he demonstrated the first JRuby version that could run Rails. Cool, I said to myself and continued with MRI.</p>

<p>I also remember myself sitting at Charles' tutorial last year trying to get my application running under JRuby. At that time I spent 3 hours to get the app almost running - all <a href="http://www.acunote.com">Acunote</a> functionality eventually worked except for burndowns because of broken RMagick. And of course our performance benchmarks showed the application became 3-5 times slower.</p>

<p>This year was a breakthrough. I was sitting through one of the presentations about scaling Rails when I thought that it's a good idea to try JRuby once again. So I did. In 15 minutes I got all JRuby stuff and required dependencies installed and Acunote running. That was a real "wow starts now" moment :) All code we have just worked. Ok, what's about the performance you might ask. I ran Acunote benchmarks and it turned out that JRuby shows <em>exactly the same performance as MRI</em>! And that's using old Java5 Client VM, postgres-pr driver written in Ruby and only 3 requests to warm up the VM. Once I'll get back home, I'll try using JDBC driver, recent JVM and issue more requests for benchmarking. The gut feeling is that JRuby will be faster in that case, watch for my next blog post if you're curious ;)</p>

<h3>Rubinius and Ruby 1.9 Story</h3>

<p>These guys have all the fun for sure, but they will simply need more time to implement their VM's right. As you see from my previous story, three years was enough to get JRuby really working. And bear in mind, they didn't have to write VM.</p>

<p>Despite that, I personally become more and more interested in Rubinius. As you might know, I'm <a href="http://kdevelop.org">KDevelop IDE</a> developer and one of the guys who worked on Ruby support for KDevelop. Java support in Eclipse is so cool just for one reason - they have compiler built in that knows <em>everything</em> about your code. Rubinius looks like a perfect tool to do exactly this for KDevelop. It may be able to load all the code and keep it constantly running as you work on the project and IDE would be able to recompile changed bits of the code on the fly. This Smalltalk-like way is probably the only way IDE can know about all symbols in the code written in dynamic language. I'm definitely looking forward having some free time to play with that.</p>

<h3>MagLev and IronRuby Story</h3>

<p>MagLev today is even less than JRuby in 2006. It's still more like a proof-of-concept that you <em>can</em> run Ruby on Smalltalk VM rather than a product in development. It was quite cool to see Smalltalk-like object caches and stores working with Ruby. As somebody said, seeing two irb's working with the same global state was a mind-blowing experience :)</p>

<p>Anyway, during the next 3-4 years we'll see whether they survive and produce something useful. The only issue in my opinion with MagLev is that it is closed source. These days you have to have really really good product to compete with open source in the arena of languages, compilers and VM's. Time will tell whether MagLev will have that advantage.</p>

<p>Microsoft's IronRuby is slightly more mature (maybe because it runs Rails ;)) but still has a long way to go. IronRuby guys also did have some cool stuff to show us at their presentation. Their mind-blowing thing was the same Ruby application running on server (Rails app) and client (Ruby in Silverlight). Sounds cool, let's see where that will go.</p>

<h2>Scaling vs Performance</h2>

<p>I thought performance was going to be the hot topic this year. I was proven wrong. The majority of Rails crowd now thinks it's easier to "scale" rather than to "optimize". Yeah, good old "add more hardware" approach with some new techniques added to make that process easier.</p>

<p><a href="http://www.koziarski.net/">Michael Koziarski</a> was probably the only one to speak about performance and tell the right thing. The idea is that it's always possible to improve something in Rails application by optimizing it or even optimizing Rails itself and the process to do that is easy - <em>measure</em> to find what's slow, <em>fix</em> it and <em>measure</em> again. </p>

<p>This measure-fix-measure is not a revelation and there're great tools to do that like ruby-prof and KCachegrind (see my <a href="http://blog.pluron.com/2008/02/memory-profilin.html">previous blog post on profiling</a>). I just wish more people used it.</p>

<p>After RailsConf Jeremy Kemper did some great work in <a href="http://weblog.rubyonrails.org/2008/6/25/living-on-the-edge-or-what-s-new-in-edge-rails-2-performance-improvements">optimizing Rails further</a>. Also it's cool to see that Jeremy added <a href="http://weblog.rubyonrails.org/2008/6/20/living-on-the-edge-or-what-s-new-in-edge-rails-1-api-changes-and-performancetests">performance test generators into Rails</a>. I do hope that those tests will help people to make performance improvement the integral part of Rails application development process.</p>

<p>Speaking about profiling, Scott Barron and Chad Humphries from EdgeCase did a nice presentation about using DTrace with Ruby. DTrace is what you can use instead of ruby-prof (or together with ruby-prof) on development machines for regular profiling. But more interestingly, thanks to its low resource footprint, DTrace can become the real-time profiling solution for your production app (in case you deploy on Leopard or Solaris). This way you would be able to find slowdowns that happen rarely or happen only under heavy load on production machines. I only wish I had such tool available on Linux (to profile <a href="http://www.acunote.com">Acunote</a> of course).</p>

<h2>Final Thanks</h2>

<p>Wrapping up this overview, I'd like to thank all speakers for great talks, especially <a href="http://yehudakatz.com/">Yehuda Katz</a> (DataMapper presentation), <a href="http://bitsweat.net/">Jeremy Kemper</a> (Rails 2.1 keynote), <a href="http://www.koziarski.net/">Michael Koziarski</a> (Rails performance), <a href="http://www.wayfargone.com/brainfloss/">Adam Pisoni</a> (Skynet) and <a href="http://www.misuse.org/science/">Stephen Midgley</a> (Complex searching in Rails) for the in-depth high-quality technical talks I've enjoyed so much.</p>

<p>I had a great time at RailsConf and hope you did that too, see you next year.</p>
</div>
</content>



    </entry>
    <entry>
        <title>Things we look forward to at RailsConf 2008</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2008/05/things-we-look.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2008/05/things-we-look.html" thr:count="2" thr:updated="2008-06-01T23:17:09-07:00" />
        <id>tag:typepad.com,2003:post-50570446</id>
        <published>2008-05-29T16:45:18-07:00</published>
        <updated>2008-05-29T16:45:18-07:00</updated>
        <summary>Yes, Gleb and I are coming again to RailsConf this year. We are expecting to listen to good talks and to meet a lot of cool people. There&#39;re definitely some interesting presentation schedule for this year. Of course we&#39;re interested...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>Yes, Gleb and I are coming again to RailsConf this year.  We are expecting to listen to good talks and to meet a lot of cool people.</p>

<p>There're definitely some interesting presentation schedule for this year. Of course we're interested in what <a href="http://en.oreilly.com/rails2008/public/schedule/detail/2529">Michael Koziarski</a> and <a href="http://en.oreilly.com/rails2008/public/schedule/detail/2021">Ezra Zygmuntowicz</a> have to say about Rails performance and scaling. This is definitely a hot topic - the whole conference program is filled up with performance/scaling talks. Looks like we should have been presenting as well, but ok, next year :)</p>

<p>It will be good to see <a href="http://en.oreilly.com/rails2008/public/schedule/detail/4356">Charles Nutter</a> and <a href="http://en.oreilly.com/rails2008/public/schedule/detail/1256">Ola Bini</a> of JRuby fame once again. We've always sympathized JRuby guys and appreciated their work, with the hope of course to run our <a href="http://www.acunote.com">Acunote</a> on top of JRuby to get some performance boost for free ;). Let's see how close we are to the goal this time...</p>

<p>What's also interesting is that Microsoft is back with <a href="http://en.oreilly.com/rails2008/public/schedule/detail/2056">IronRuby</a>. We're intrigued whether John Lam and Jimmy Schementi hacked it enough to run Rails or not.</p>

<p>We're also eager to hear what Bob Walker and Avi Bryant baked up with <a href="http://en.oreilly.com/rails2008/public/schedule/detail/2056">MagLev Ruby VM</a> and how far Evan Phoenix and his team progressed with <a href="http://en.oreilly.com/rails2008/public/schedule/detail/4342">Rubinius</a>.</p>

<p>For sure we're not only interested in alternative Ruby implementations. Scott Barron and Chad Humphries prepared a promising talk about profiling applications with <a href="http://en.oreilly.com/rails2008/public/schedule/detail/1945">DTrace on OSX</a>. Apart from Linux desktops we do use some MacBook's for development and might benefit from being able to use DTrace. Yehuda Katz's <a href="http://en.oreilly.com/rails2008/public/schedule/detail/1883">DataMapper</a> and Adam Pisoni's <a href="http://en.oreilly.com/rails2008/public/schedule/detail/2022">Skynet - A Ruby Map/Reduce Framework</a> talks look exciting as well. </p>

<p>So, we hope to learn something new and have fun in Portland once again this year. See you at RailsConf!</p>
</div>
</content>



    </entry>
    <entry>
        <title>Acunote for Google Summer of Code Projects</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2008/04/acunote-for-goo.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2008/04/acunote-for-goo.html" thr:count="1" thr:updated="2008-04-23T20:08:20-07:00" />
        <id>tag:typepad.com,2003:post-48895568</id>
        <published>2008-04-23T09:37:56-07:00</published>
        <updated>2008-04-23T09:37:56-07:00</updated>
        <summary>We in Pluron are great believers in open source. We develop and deploy with open source tools on open source platforms and we were always keen to give something back to the community. We&#39;ve been [sponsoring KDE developer](http://adymo.blogspot.com/2006/06/my-job-is-my-passion-or-kdevelop.html) for two...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        <category scheme="http://www.sixapart.com/ns/types#category" term="Acunote" />
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>We in Pluron are great believers in open source. We develop and deploy with open source tools on open source platforms and we were always keen to give something back to the community. We've been <a href="http://adymo.blogspot.com/2006/06/my-job-is-my-passion-or-kdevelop.html">sponsoring KDE developer</a> for two years, we've released several parts of our product - <a href="http://www.acunote.com/open-source/javascript-keyboard-shortcuts">Acunote Shortcuts</a>, <a href="http://mediacloth.rubyforge.org">Mediacloth</a> and <a href="http://code.google.com/p/activerecpp/">ActiveReC++</a> under open source MIT license. But of course that's not everything we did.</p>

<p>We made Acunote <a href="http://www.acunote.com/open-source/acunote-for-open-source">free</a> for open source projects. Starting with summer 2007 we also offered Acunote access for Google Summer of Code program participants. </p>

<p>Google is running <a href="http://code.google.com/soc">Summer of Code program</a> for the fourth year now. Students who participate in this program write open source code but have their own designated projects with a deadline and a mentor assigned. The mentor needs to keep track of student's progress and has to review his code. And this is when Acunote becomes useful for both mentors and students because of its strong task management and <a href="http://www.acunote.com/tour/code_inspection">code review</a> capabilities.</p>

<p>Last year <a href="http://www.acunote.com/open-source/summer-of-code-projects">14 GSoC projects</a> used Acunote and we got some very positive feedback. This year we of course continue offering our lightweight and cool software project management tool for GSoC'ers. </p>

<p>So, if you are a student working on Summer of Code project or a mentor keeping an eye on such project, 
take look at our <a href="http://www.acunote.com/open-source/summer-of-code-projects">Acunote for GSoC HOWTO</a> page to learn how you can use it and <a href="http://www.acunote.com/open-source/summer-of-code-signup">signup for free</a>!</p>
</div>
</content>



    </entry>
    <entry>
        <title>Pluron Rails Optimization Performance @ UA Web</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2008/03/pluron-rails-op.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2008/03/pluron-rails-op.html" thr:count="1" thr:updated="2008-04-25T00:21:45-07:00" />
        <id>tag:typepad.com,2003:post-47476494</id>
        <published>2008-03-25T10:38:08-07:00</published>
        <updated>2008-03-25T10:38:08-07:00</updated>
        <summary>Hi there. I will be giving a presentation on our Rails performance optimization work @ UA Web conference this Friday at President Hotel, Kiev, Ukraine . If you are a reader, please come by and say &#39;Hi.&#39;</summary>
        <author>
            <name>Serge Smetana</name>
        </author>
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>Hi there. I will be giving a presentation on our Rails performance optimization work @ <a href="http://uaweb.in.ua/news/4702.html">UA Web</a> conference this Friday  at <a href="http://uaweb.in.ua/delegates/3495.html">President Hotel</a>, Kiev, Ukraine .  If you are a reader, please come by and say 'Hi.'</p>
</div>
</content>



    </entry>
    <entry>
        <title>Make Rails Associations Faster by Optimizing Named Blocks and String Callbacks</title>
        <link rel="alternate" type="text/html" href="http://blog.pluron.com/2008/02/rails-faster-as.html" />
        <link rel="replies" type="text/html" href="http://blog.pluron.com/2008/02/rails-faster-as.html" thr:count="16" thr:updated="2008-08-11T13:31:42-07:00" />
        <id>tag:typepad.com,2003:post-45571736</id>
        <published>2008-02-13T12:01:07-08:00</published>
        <updated>2008-02-13T12:01:07-08:00</updated>
        <summary>In our [previous](http://blog.pluron.com/2008/01/ruby-on-rails-i.html) [articles](http://blog.pluron.com/2008/01/guerrillas-way.html) we described how Rails spends much of its time garbage collecting, and that significant speedup can be achieved by [memory profiling](http://blog.pluron.com/2008/02/memory-profilin.html) and fixing memory allocation hotspots. In this article, we&#39;ll describe couple more such hotposts dealing...</summary>
        <author>
            <name>Alexander Dymo</name>
        </author>
        <category scheme="http://www.sixapart.com/ns/types#category" term="Rails Performance" />
        
        
<content type="xhtml" xml:lang="en-US" xml:base="http://blog.pluron.com/">
<div xmlns="http://www.w3.org/1999/xhtml"><p>In our <a href="http://blog.pluron.com/2008/01/ruby-on-rails-i.html">previous</a>
<a href="http://blog.pluron.com/2008/01/guerrillas-way.html">articles</a> we
described how Rails spends much of its time garbage collecting, and
that significant speedup can be achieved by <a href="http://blog.pluron.com/2008/02/memory-profilin.html">memory
profiling</a> and
fixing memory allocation hotspots.  In this article, we'll describe
couple more such hotposts dealing with named block parameters and
associations, and provide the patches.</p>

<h2>Named Block Parameters Considered Harmful (for Performance)</h2>

<p>We already <a href="http://blog.pluron.com/2008/01/ruby-on-rails-i.html#patches">wrote</a> that passing a block to a method of <code>ActiveRecord::Associations::HasManyAssociation</code> instance and its friends chews up the memory. For example, a single call to <code>association.select { |record| record.new_record? }</code> can allocate up to 10K of memory depending on the association size. A brief look at <a href="http://dev.rubyonrails.org/browser/tags/rel_2-0-2/activerecord/lib/active_record/associations">associations source</a> reveals that Rails itself has similar code in many places.</p>

<p>Each association is a proxy to the actual array of associated object(s). It seems like method_missing is a good way to implement proxy pattern in Ruby and indeed that's what Rails does. The proxy contains an array of associated objects and sends all missing methods in the proxy into that array. If we simplify the Rails code, we'll see something like this:</p>

<pre><code>class Association
    def method_missing(method, *args, &amp;block)
        @array.send(method, *args, &amp;block)
    end
end
</code></pre>

<p>At first, we couldn't understand why this would be slow, but after some digging we got it.  Each named <code>&amp;block</code> parameter requires extra processing. Ruby creates a <code>Proc</code> object that represents the block passed and adds a <code>Binding</code> object with the local execution context to that <code>Proc</code>. In an empty Ruby script without any variables defined binding will be around 400 bytes. In actual Rails application bindings may grow up to 10K in size. Now imagine you're doing something with AR object and its association in a loop 100 times. Bah! 1 megabyte of memory is gone.</p>

<p>Each Ruby block is a closure, and it captures its complete environment at the time of creation.  Ola Bini has a great <a href="http://ola-bini.blogspot.com/2007/12/ruby-closures-and-memory-usage.html">article</a> on this.  So is all hope lost?  No -- turns out that MRI has different implementations for named and anonymous block parameters.  When calling a function which takes anonymous block, it simply stores a reference to the caller's stack frame.  It's OK to do that since the callee is guaranteed to exit before caller's stack frame is popped.  When calling a function that takes a named block MRI assumes that this block may be long-lived and clones the environment right there.  So anonymous block parameters are much more efficient than named block parameters.  Also see related <a href="http://www.ruby-forum.com/topic/71221">discussion</a> on Ruby Forum.</p>

<p>The optimization to Rails Association is simple - just pass a new block and yield the old one inside:</p>

<pre><code>class Association
    def method_missing(method, *args)
        @array.send(method, *args) { |*block_args| yield(*block_args) if block_given? }
    end
end
</code></pre>

<p>This not only saves memory, but runs faster. I've benchmarked that on <a href="http://www.acunote.com">Acunote</a> copying 120 objects (each with 6 associations) using ActiveRecord.</p>

<p>With named block parameters:</p>

<pre><code>Benchmark Copy 120
memory: 97527K total in 1698240 allocations, GC calls: 13, GC time: 977 msec
time: 3.25 ± 0.05
</code></pre>

<p>With yields:</p>

<pre><code>Benchmark Copy 120
memory: 92670K total in 1636677 allocations, GC calls: 12, GC time: 901 msec
time: 3.15 ± 0.05
</code></pre>

<p>As the result, 5 megabytes of memory and 100msec saved for good.</p>

<h4>That's Cool! Where's The Patch?</h4>

<ul>
<li><a href="http://pluron.typepad.com/pluron/patches/no_block_args_in_associations.patch">Patch for Rails 1.2</a></li>
<li><p><a href="http://dev.rubyonrails.org/ticket/11109">Patch for Rails 2 (trunk)</a></p>

<ul>
<li>Patch accepted and committed into <a href="http://dev.rubyonrails.org/changeset/8865">trunk</a> and <a href="http://dev.rubyonrails.org/changeset/8866">2.0-stable branch</a> by <a href="http://weblog.techno-weenie.net/">Rick Olson</a></li>
</ul></li>
<li><p>Fix your code and remove <code>&amp;block</code>'s where you can</p></li>
</ul>

<h2>String Callbacks Considered Harmful (for Performance)</h2>

<p>This one is even more interesting. Rails allows for string callbacks in <code>before_save</code>, <code>after_save</code>, <code>before_destroy</code> and so on in ActiveRecord models. Each such callback is a string that is evaluated in the context of AR object. Let me cite Rails <a href="http://dev.rubyonrails.org/browser/tags/rel_2-0-2/activerecord/lib/active_record/associations">callbacks.rb</a> source here:</p>

<pre><code>...
def callback(method)
    notify(method)

    callbacks_for(method).each do |callback|
        result = case callback
            when Symbol
                self.send(callback)
            when String
                eval(callback, binding)
            when Proc, Method
                callback.call(self)
            else
            ...
</code></pre>

<p>You see, to evaluate the string we need to get the binding. And as we all remember from our named block parameter discussion, the binding takes memory.  Even when you don't use string callbacks yourself, Rails associations automatically create them for you.</p>

<p>For example, <code>has_many</code> will define 4 string callbacks. You'll get <code>before_save</code>, <code>after_create</code> and <code>after_update</code> to assure that new associated records are saved when its parent record is saved; and also you'll get one for <code>before_destroy</code> that destroys dependent objects or nullifies their foreign keys.</p>

<p>Rewriting string callbacks into symbol callbacks gives a tangible performance boost. I did that change and benchmarked Acunote again.</p>

<p>With string callbacks in associations:</p>

<pre><code>    Benchmark Copy 120
    memory: 92670K total in 1636677 allocations, GC calls: 12, GC time: 901 msec
    time: 3.15 ± 0.05
</code></pre>

<p>With symbol callbacks in associations:</p>

<pre><code>    Benchmark Copy 120
    memory: 39108K total in 944764 allocations, GC calls: 6, GC time: 479 msec
    time: 2.45 ± 0.05
</code></pre>

<p>Whoa! Rewriting string callbacks to symbol callbacks saved <b>52 megabytes</b> and gave <b>0.7 sec</b> speedup. Nice!</p>

<h4>That's Cool! Where The Patch?</h4>

<ul>
<li><a href="http://pluron.typepad.com/pluron/patches/no_string_callbacks_in_associations.patch">Patch for Rails 1.2</a></li>
<li><a href="http://dev.rubyonrails.org/ticket/11108">Patch for Rails 2 (trunk)</a>
<ul>
<li>Patch accepted and committed into <a href="http://dev.rubyonrails.org/changeset/8867">trunk</a> by <a href="http://weblog.techno-weenie.net/">Rick Olson</a></li>
</ul></li>
<li>Fix your code and stop using string callbacks</li>
</ul>
</div>
</content>



    </entry>
 
</feed>

<!-- ph=1 -->
