Living on the edge (of Rails) #8 – the code optimization edition

In: Edge Rails|Open Source|Ruby|Ruby on Rails

19 Feb 2008

Here’s this week’s update of the important changes that have been happening on edge Rails. There were lots of performance patches this week, credit mostly due to the guys from Acunote and their latest blog post on optimizing Rails.

This week’s report covers changes from 11 Feb 2008 to 17 Feb 2008 (the day the corresponding Rails Envy podcast was recorded).

ActiveRecord::Base#attributes-related optimizations

The ActiveRecord::Base#attributes_before_typecast no longer clones attribute unnecessarily.

Related changeset:

ActiveRecord::Base#attributes now no longer supports options. The ActiveRecord::Base#attributes method used to support :only and :except options, which are actually unnecessary since it returns a Hash. You can use Hash#slice or Hash#except (both ActiveSupport core extensions) directly on the hash.

Related changeset:

ActiveRecord associations optimized by avoiding named block arguments

Alexander Dymo is at it again optimizing ActiveRecord performance, this time by avoiding passing named block arguments. Turns out that it is faster and more memory efficient in Ruby to pass blocks like this:

def foo
  bar { |*block_args| yield(*block_args) if block_given?

instead of passing the block around like so:

def foo(&&block)

Occurrences of the more expensive way of passing blocks have been replaced in the ActiveRecord associations code so associations are lighter to use.

For a more detailed write-up, check out Alexander’s blog entry.

Related changeset:

ActiveRecord associations optimized by using symbol callbacks instead of string callbacks

Again from Alexander Dymo, this patch also improving ActiveRecord association performance. Internally, ActiveRecord associations code uses a lot of string callbacks (e.g. before_save "some_ruby_code_to_eval"). It turns out that that is significantly more expensive compared to simply using Module#define_method because when the callback string is evaluated, the binding needs to be passed along. Passing the binding takes up memory (which is bad, of course).

The internal associations code has been changed to use that define_method now, and if you’re using a lot of associations, you would save megabytes of memory! (Alexander’s blog post has more detailed benchmarks.)

Remember, when writing your own before and after filters, not to use a string that needs to be evaled!

Related changeset:

Improve ActiveRecord::Base#save performance by avoiding repeated calls to ActiveRecord::Base#connection

Another Alexander Dymo performance patch. This particular patch improves performance by getting the connection object just once and calling it directly, instead of needlessly calling several times. This should improve ActiveRecord::Base#save performance.

Related changeset:

ActiveRecord associations now support the :readonly option

ActiveRecord::Base#find supports the :readonly option to retrieve records that were read-only (i.e. you couldn’t save them). Now you can do the same for associations, like so:

has_many :comments, :readonly => true

This allows you to protect associated records from being saved.

Related changeset:

String#squish and String#squish! to remove consecutive chunks of whitespace

A String#squish extension has been added that trims whitespace from both ends of a string, and compressing consecutive whitespace groups within the string into 1 space each.

For example,

@description = %{ String#squish() allows me to write long strings
  inside my Ruby code, without having to worry about indentation,
  the page margin, spaces, tabs or newlines. "double" or 'single'
  quotes are both fine, and #{interpolation} works. }.squish

Related changeset:

As always, let me know of any inaccuracies or any suggestions you may have in the comments, see you guys next week!

1 Response to Living on the edge (of Rails) #8 – the code optimization edition


Jan De Poorter

February 20th, 2008 at 5pm

Changeset 8865 has been reverted in changeset 8874 because it broke some stuff, and needed more tests.