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.
Now let’s take a look at your application.rb and measure how much time we’re spending loading gems.
When we run this benchmark we get an interesting result:
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::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.
- Begin with a minor gem. By that I mean a gem that is only used in one or two files or that has minimal impact on your application. That’s usually a good starting point because the effects are minor. Also, don’t pick a gem that rails automatically includes anyways – that won’t work.
- Check if the gem has Railties. One way to do this is to navigate to its source code directory and search for uses of ‘Railtie’.
- If you see an output then that gem uses Railties and you need to move on to another gem for this optimization.
- If you don’t see an output then the gem does not use Railties. That means it doesn’t hook into the Rails initialization process, which in turn means that it does not need to be required during the application boot. So, let’s take care of that – tell bundler to not require that gem.
- Find uses of that gem, and manually require the gem in those files.
- Run your tests.
- Run your benchmarks.
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!