Making CGI Rails Faster
Rails’ preferred installation method is RubyGems, a convenient, powerful utility for installing Ruby libraries and applications. Unfortunately, that convenience comes at a price—loading a library via RubyGems adds some overhead. Recent releases of RubyGems have made some good progress in reducing that overhead, but it still exists.
Rails, being (by default) a gem-dependent application, incurs that overhead every time you load the app “clean” (ie, not under FCGI or such). For example, my in-progress financial package manager (Budget-Wise) took about 2.4 seconds just to load the login screen!
I found that by bypassing RubyGems altogether, the load time was cut by more than half, from 2.4 seconds to just over 1.1 seconds. If you run Rails in CGI mode and want an option for speeding your response time, consider making the following change to your environment.rb file:
# Require Rails gems.
#require 'rubygems'
#require_gem 'activerecord'
#require_gem 'actionpack'
#require_gem 'actionmailer'
#require_gem 'rails'
GEM_DIR = "/usr/local/lib/ruby/gems/1.8/gems"
$:.push "#{GEM_DIR}/activerecord-1.5.1/lib"
$:.push "#{GEM_DIR}/actionpack-1.3.1/lib"
$:.push "#{GEM_DIR}/actionmailer-0.6.1/lib"
$:.push "#{GEM_DIR}/rails-0.9.4.1/lib"
require 'active_record'
require 'action_controller'
require 'action_view'
require 'action_mailer'
In other words—just comment out the part that requires the gems, and then manually add the necessary directories to your load path. Then, just require those libs you need explicitly.
The drawback: you have to change your environment.rb
file every time you upgrade any part of rails, but if that’s not an issue, you might find this a worthy enhancement to your development cycle. Of course, if you are using WEBrick or FCGI, this isn’t an issue at all since the gem require only happens once. You’ll only really be benefited if you use Rails in CGI mode.
Update (24 Jan 2005): Apparently, TextDrive is still using version 0.8.1 of RubyGems. Using RubyGems 0.8.4 gives you just about the same speed as requiring the libraries directly, so if you are using RubyGems 0.8.4, you do not need to apply the “optimization” described above. If, on the other hand, you don’t have the option of installing a newer version of RubyGems, consider making the following additional optimization, which cut the total load time for me down to less than 0.5 seconds. Create a file in your application’s lib
directory called rubygems.rb
, and put the following code in it:
raise LoadError, "place holder"
This prevents the ActionView library from requiring RubyGems, which it attempts to do explicitly in order to use the Builder gem. If RubyGems can’t be found, ActionView will load an alternative copy of the Builder library and continue on. Thus, adding the above place holder file just prevents RubyGems from being loaded at all, anywhere, by your app or by any of its dependencies.
Again, if you’re using 0.8.4 (or newer) of RubyGems, this is not an issue. (Thanks to Chad and Jim for pointing this out!)