This site is dedicated to further knowledge about creating Ruby on Rails applications professionaly. We discuss Ruby on Rails features from a performance angle, discuss Ruby on Rails performance analysis methods, provide information on Ruby on Rails scaling and benchmark Ruby on Rails performance for each release. We discuss best practices for selecting Ruby on Rails session containers, fragment and page caching and optimizing database queries.

Accelerate your dates

Posted 09 Nov 2005

When I started work on improving my app's speed, I noticed that on pages that display lots of date information, a large percentage of CPU time was spent on formatting date strings. I added a quick fix to my code, was satisifed and quickly forgot about it.

But recently I read a message on the ruby talk mailing list, where someone more or less rejected Rails after writing a small Ruby program for log file analysis that ran for about 27 minutes on a 100MB log file and a literally translated Java program finished in only 2.5 minutes blog entry.

It turned out that the inefficiency of the Ruby script was caused by the Ruby Date class, as someone rewrote the program using regexps to parse date info that needed only 3 minutes to complete the task.

I'm pretty sure that the Date class performance could be much improved, for example by rewriting it in C, but we'll leave this to the ruby core team. For page speedup we simply resort to avoiding construction of Date objects altogether. For Mysql, the Rails database layer will always return date information as strings. The strings are formatted as 'YYYY-mm-dd', i.e. they will have ISO date format. Such a string can of course be turned into 'dd.mm.YYYY' easily using Ruby primitives. So instead of writing

<%= r.created_at.strftime "dd.mm.YYYY" %>

or

<%= r.created_at %>

we'll change this into

<%= r.created_at_formatted %>

and add a function created_at_formatted to our model, which will access the attributes hash directly and transform the string into our desired format. One possible way to code this function is

def created_at_formatted
  d = @attributes['created_at']
  "#{d[8,2]}.#{d[5,2]}.#{d[0,4]}"
end

another is

def created_at_formatted
  @attributes['created_at'] =~ /^(\d\d\d\d)-(\d\d)-(\d\d)/
  "#{$3}.#{$2}.#{$1}"
end

This assumes that created_at is a non null column. If it isn't, you'll need to add some guards for null values.

The first version comes out slightly faster than the second, which I expected. But how much faster than the original code?

On pages with 25 dates displayed it is approximately 70% faster than the original version. On a page with 1 date, it's already 6% faster.

If you have pages with a large number of date displays, you might consider this technique to make them a tad snappier :-)

Update: As mentioned by henri, the technique does not apply to the oracle adapter, as it returns Time objects. As time permits I will look into the other adapters, to find the "bad boys" which return strings.

Posted in blog | Tags

Comments

blog comments powered by Disqus