However much we buy into the adage that hardware is cheaper than developers, we all still need to make our code as responsive as possible. We tune the database, we refactor our code, and try to find the optimum balance between developer time, code legibility, and performance. In the process, many of us have found that rails’ rendering, particularly the Erb library is one of its slowest parts. Enter Erubis.

Erubis is an implementation of eRuby that replaces Erb. It’s written in C and claims to be three times faster than Erb, and 10% faster than its nearest competitor ( eruby from mod_ruby which isn’t recommended for use with rails apps). I’ve been trying it out over the past few days, initially with version 2.1 and today with 2.2, and it seemed like time for a report.

Updating an existing rails app is pretty straightforward. I’ve heard that there are some issues to watch out for, but mostly all it takes to convert an existing app is to follow the simple instructions and add:

require 'erubis/helpers/rails_helper'

inside your environment.rb file. Then restart the server, and away you go.

I’ve been testing it out on an app that is fairly early in its lifecycle and hasn’t yet seen much tuning. The app suffers from running too many database queries, but even so it spends more of its time in the rendering than in the database. Running locally off one mongrel I found that, I tested using ‘ab’ from a parallels virtual machine, and found that over 250 requests my standard setup got 7.55 requests/second. Switching to Erubis made little difference to that, which came as quite a surprise.

I then tried applying the patch to rails that the documentation offers as an optional extra. That’s when I saw the real performance leap. Just that change pushed the app up to 13.02 requests/second. I tried it again, and saw 16.29 requests/second. Not quite three times the speed, but doubles not something to be sniffed at. There’s just the small matter of patching rails, which isn’t my first choice as I like to keep this app in step with edge.

But of course this is ruby, so there are alternatives to patching. We can add the new methods in environment.rb before we require the erubis helper and it’ll do the same job.

module ActionView
  class Base
    def convert_template_into_ruby_code(template)
      ERB.new(template, nil, @@erb_trim_mode).src
    end

    # Create source code for given template
    def create_template_source(extension, template, render_symbol, locals)
      if template_requires_setup?(extension)
        body = case extension.to_sym
          when :rxml
            "controller.response.content_type ||= 'application/xml'n" +
            "xml = Builder::XmlMarkup.new(:indent => 2)n" +
            template
          when :rjs
            "controller.response.content_type ||= 'text/javascript'n" +
            "update_page do |page|n#{template}nend"
        end
      else
        body = convert_template_into_ruby_code(template)
      end

      @@template_args[render_symbol] ||= {}
      locals_keys = @@template_args[render_symbol].keys | locals
      @@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }

      locals_code = ""
      locals_keys.each do |key|
        locals_code < < "#{key} = local_assigns[:#{key}]n"
      end

      "def #{render_symbol}(local_assigns)n#{locals_code}#{body}nend"
    end
  end
end

So far, I’m impressed.

Update (17th Feb): I had misread the information and not understood that it is written entirely in ruby. And apparently 2.2 doesn’t have the same issues in rails that 2.1 had.