This simple improvement can have varying effects on the application depending on how many Gems you require. Before you actually jump into this I would definitely recommend having a full unit-test suite. Anytime you're refactoring code or changing how dependencies are loaded you want to be sure you aren't messing things up.
The examples from this post were performed on a Rails 3.1.3 application running on Ruby 1.9.3 on a Mac OSX PowerBook. The Gemfile contained about 65 gems in development mode.
The first step to any performance improvement endeavor is to benchmark. It's also important that you perform this step more than once - this gives you a more consistent reading as the first run can often be inflated above the average run-time.
(master) % time bundle exec rake environment
bundle exec rake environment 29.09s user 2.44s system 61% cpu 51.570 total
(master) % time bundle exec rake environment
bundle exec rake environment 28.02s user 1.77s system 98% cpu 30.184 total
(master) % time bundle exec rake environment
bundle exec rake environment 28.24s user 1.84s system 98% cpu 30.632 total
Now let's take a look at your application.rb and measure how much time we're spending loading gems.
# application.rb
require 'benchmark'
Benchmark.bm do |x|
x.report do
if defined?(Bundler)
Bundler.require(*Rails.groups(:assets => %w(development test)))
end
end
end
When we run this benchmark we get an interesting result:
(master) % time bundle exec rake environment
user system total real
10.910000 0.900000 11.810000 ( 11.969414)
bundle exec rake environment 27.99s user 1.76s system 98% cpu 30.199 total
(master) % time bundle exec rake environment
user system total real
10.770000 0.900000 11.670000 ( 11.721163)
bundle exec rake environment 27.71s user 1.78s system 99% cpu 29.721 total
We're spending about 1/3 of our time in this Bundler.require statement. The
reason for this is that Bundler is loading and executing every gem in your
Gemfile, and with 65 gems that's a hefty portion of time spent executing code.
We can confirm this by benchmarking the two parts of Bundler.require -
Bundler::Runtime#setup and Bundler::Runtime#require. I'm going to leave out
the details, but require is the offender here, and that makes sense. Bundler
isn't really doing anything fancy, but it is responsible for executing all those
gems you've collected in your Gemfile.
Here's a simple way to improve that.
cd `bundle list flickr_fu`
find . | xargs grep Railtie
# Gemfile
gem 'flickr_fu', require: false
# lib/flickr_decorator.rb
require 'flickr_fu'
module FlickrDecorator
...
end
You probably won't notice a difference form a few measly gems, but once you're done with the entire process you'll be looking at a significant time improvement. In this particular application I was able to shave 10s off each application boot. There's an added benefit that you now know exactly which files are using which gems. This makes it easier to test these files in isolation and measure the impact/use of a gem. Enjoy!