Top 10 Ruby on Rails performance tips

Please note that this article is now obsolete.

The performance of Ruby on Rails is influenced by many factors, particularly the configuration of your deployment server(s). However the application code can make a big difference and determine whether your site is slow or highly responsive. This short article is about some of the tips and best coding practices to improve performances in Rails only, and won’t attempt to cover the server configuration improvements for the various deployments options.

  1. Optimize your Ruby code: this may seem obvious, but a Rails application is essentially ruby code that will have to be run. Make sure your code is efficient from a Ruby standpoint. Take a look at your code and ask yourself if some refactoring is in order, keeping in mind performance considerations and algorithmic efficiency. Profiling tools are, of course, very helpful in identifying slow code, but the following are some general considerations (some of them may appear admittedly obvious to you):
    • When available use the built-in classes and methods, rather than rolling your own;
    • Use Regular Expressions rather than costly loops, when you need to parse and process all but the smallest text;
    • Use Libxml rather than the slower REXML if you are processing XML documents;
    • Sometimes you may want to trade off just a bit of elegance and abstraction for speed (e.g. define_method and yield can be costly);
    • The best way to resolve slow loops, is to remove them if possible. Not always, but in a few cases you can avoid loops by restructuring your code;
    • Simplify and reduce nested if/unless as much as you can and remember that the operator ||= is your friend;
    • Hashes are expensive data structures. Consider storing the value for a given key in a local variable if you need to recall the value a few times. More in general, it’s a good idea to store in a variable (local, instance or class variable) any frequently accessed data structure.
  2. Caching is good: caching can significantly speed up your application. In particular:
  3. Use your database to the full extent of the law 🙂: don’t be afraid of using the cool features provided by your database, even if they are not directly supported by Rails and doing so means bypassing ActiveRecord. For example define stored procedures and functions, knowing that you can use them by communicating directly with the database through driver calls, rather than ActiveRecord high level methods. This can hugely improve the performance of a data bound Rails application.
  4. Finders are great but be careful: finders are very pleasant to use, enable you to write readable code and they don’t require in-depth SQL knowledge. But the nice high level abstraction come with a computational cost. Follow these rules of thumb:
    • Retrieve only the information that you need. A lot of execution time can be wasted by running selects for data that is not really needed. When using the various finders make sure to provide the right options to select only the fields required (:select), and if you only need a numbered subset of records from the resultset, opportunely specify a limit (with the :limit and :offset options).
    • Don’t kill your database with too many queries, use eager loading of associations through the include option:
      # This will generates only one query,
      # rather than Post.count + 1 queries
      for post in Post.find(:all,
                            :include => [ :author, :comments ])
        # Do something with post
      end
    • Avoid dynamic finders like MyModel.find_by_*. While using something like User.find_by_username is very readable and easy, it also can cost you a lot. In fact, ActiveRecord dynamically generates these methods within method_missing and this can be quite slow. In fact, once the method is defined and invoked, the mapping with the model attribute (username in our example) is ultimately achieved through a select query which is built before being sent to the database. Using MyModel.find_by_sql directly, or even MyModel.find, is much more efficient;
    • Be sure to use MyModel.find_by_sql whenever you need to run an optimized SQL query. Needless to say, even if the final SQL statement ends up being the same, find_by_sql is more efficient than the equivalent find (no need to build the actual SQL string from the various option passed to the method). If you are building a plugin that needs to be cross-platform though, verify that the SQL queries will run on all Rails supported databases, or just use find instead. In general, using find is more readable and leads to better maintainable code, so before starting to fill your application with find_by_sql, do some profiling and individuate slow queries which may need to be customized and optimized manually.
  5. Group operations in a transaction: ActiveRecord wraps the creation or update of a record in a single transaction. Multiple inserts will then generate many transactions (one for each insert). Grouping multiple inserts in one single transaction will speed things up.

    Insead of:

     my_collection.each do |q|
       Quote.create({:phrase => q})
     end

    Use:

    Quote.transaction do
     my_collection.each do |q|
       Quote.create({:phrase => q})
     end
    end

    or for rolling back the whole transaction if any insert fails, use:

    Quote.transaction do
     my_collection.each do |q|
       quote = Quote.new({:phrase => q})
       quote.save!
     end
    end
  6. Control your controllers: filters are expensive, don’t abuse them. Also, don’t overuse too many instance variables that are not actually required by your views (they are not light).
  7. Use HTML for your views: in your view templates don’t overuse helpers. Every time you use form helpers you are introducing an extra step. Do you really need a helper to write the HTML for a link, a textbox or a form for you? (You may even make your designer, who doesn’t know Ruby, happy!)
  8. Logging: configure your applications so that they log only the information that is absolutely vital to you. Logging is an expensive operation and an inappropriate level (e.g. Logger::DEBUG) can cripple your production application.
  9. Patch the GC: OK, not really a coding issue, but patching Ruby’s Garbage Collection is strongly advised and will improve the speed of your Ruby and Rails applications significantly.
  10. A final note:I don’t advocate premature optimization, but if you can, work on your code with these principles in mind (but don’t overdo it either). Last minute changes and tweaks are possible but less desirable than a “performance aware” style of coding. Profile your applications, benchmark them
    and have fun experimenting.

Get more stuff like this

Subscribe to my mailing list to receive similar updates about programming.

Thank you for subscribing. Please check your email to confirm your subscription.

Something went wrong.

25 Comments

  1. evan February 13, 2007
  2. josh February 13, 2007
  3. Tyler February 13, 2007
  4. PJ Hyett February 13, 2007
  5. Mazdak February 13, 2007
  6. big o February 13, 2007
  7. Ryan Allen February 14, 2007
  8. Mark Carey February 14, 2007
  9. David February 14, 2007
  10. dejan.dimic@gmail.com February 14, 2007
  11. Spif February 14, 2007
  12. Antonio Cangiano February 14, 2007
  13. tomafro February 14, 2007
  14. Patrick February 14, 2007
  15. Josh February 14, 2007
  16. pireland February 14, 2007
  17. Bradley February 14, 2007
  18. Rather not say February 15, 2007
  19. Rich Collins February 15, 2007
  20. Ricardo February 15, 2007
  21. Chris Kilmer February 16, 2007
  22. Nicole February 23, 2007
  23. Anders February 28, 2007
  24. Roger Pack April 23, 2007
  25. Ruby on Rails Examples May 10, 2007