Xavier Shay blogs here

In which I provide easy instructions to try a new patch that drastically improves the start up time of Ruby applications, in the hope that with wide support it will be merged into the upcoming 1.9.3 release. Skip to the bottom for instructions, or keep reading for the narrative.

UPDATE: If you have trouble installing, grab a recent copy of rvm: rvm get head.

Background

Recent releases of MRI Ruby have introduced some fairly major performance regressions when requiring files:

For reference, our medium-sized Rails application requires around 2200 files &emdash; off the right-hand side of this graph. This is problematic. On 1.9.2 it takes 20s to start up, on 1.9.3 it takes 46s. Both are far too long.

There are a few reasons for this, but the core of the problem is the basic algorithm which looks something like this:

That’s just a synthetic benchmark, but it works in the real world too. My main Rails application now loads in a mite over 10s, down from 20s it was taking on 1.9.2. A blank Rails app loads in 1.1s, which is even faster than 1.8.7.

Getting the fix

Here is how you can try out my patch right now in just ten minutes using RVM.

Next steps

I imagine there will be a bit more work to get this into Ruby 1.9.3, but after that this is just the first step of many to try and speed up the time Rails takes to start up. Bundler and RubyGems still spend a lot of time doing … something, which I want to investigate. I also want to port these changes over to JRuby which has similar issues (Rubinius isn’t quite as fast out of the gate, but does not degrade exponentially so would not benefit from this patch).

Gem files will remain installed in /Users/mech/.rvm/gems/ruby-head-patched/gems/RedCloth-4.2.7 for inspection.
Results logged to /Users/mech/.rvm/gems/ruby-head-patched/gems/RedCloth-4.2.7/ext/redcloth_scan/gem_make.out
from /Users/mech/.rvm/rubies/ruby-head-patched/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:511:in `block in build_extensions'
from /Users/mech/.rvm/rubies/ruby-head-patched/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:486:in `each'
from /Users/mech/.rvm/rubies/ruby-head-patched/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:486:in `build_extensions'
from /Users/mech/.rvm/rubies/ruby-head-patched/lib/ruby/site_ruby/1.9.1/rubygems/installer.rb:159:in `install'
from /Users/mech/.rvm/gems/ruby-head-patched/gems/bundler-1.0.14/lib/bundler/source.rb:101:in `block in install'
from /Users/mech/.rvm/gems/ruby-head-patched/gems/bundler-1.0.14/lib/bundler/rubygems_integration.rb:78:in `preserve_paths'
from /Users/mech/.rvm/gems/rub

To compare apples to apples (rather than apples to ruby-1.9.2-p180) I built ruby-head from source with and without your patch, installed all gems etc, and compared my Rails 3.0.7 app as a median of 10 runs of "rails runner nil".

Had a bit of a headache getting the patch to install,
Firstly I had an error with Bison "bison is not available in your path."
This was fixed with a sudo apt-get install bison.
Then another issue "ERROR: rvm requires autoreconf to install the selected ruby interpreter however autoreconf was not found in the PATH.".
This was fixed with
sudo apt-get install autoconf

So my results on a 3.0.8.rc1 app are
.rvm/gems/ruby-1.8.7-p302
time script/rails runner "puts 1"
1

@Kain: I faced the same problem. It seems like a bug in RVM. I tried installing 1.8.7 once but didn't finish due to a issue with current Ruby and Debian unstable. When I tried installing ruby-head it was resolving to 1.8.7. Then I decided to manually patch Ruby 1.8.7 and finished installing it. After that ruby-head was working again. Try installing 1.8.7 first.

I've been using a 1.9.2-p180 with your patch and been happy with the results.
I was looking forward to seeing a similar speed-up in the just released 1.9.3-preview1, but alas it just isn't there. In fact, the preview of 1.9.3 behaves almost identically to an unpatched 1.9.2. Which means roughly around 15 seconds start up for my rails app, compared to around 10 when using 1.9.2-p180 with the patch. I know there's a couple alternate patches floating around (haven't tried them myself) but seriously, someone upstream needs to pick one that works and get it into the official release. In the meantime, I guess I'm still using that manually patched 1.9.2.

@michael I'm surprised you didn't see a difference in 1.9.3-preview1. There is a different patch in there which should speed things up. If you have a moment please do report your timings to the ruby-core mailing list, they will really appreciate it.

There is another blog posting on this relating to a patch from the Ruby core team as it relates to faster loading, and a subsequent patch that can be applied to 1.9.2-p290:
Here's the blog posting: http://www.rubyinside.com/ruby-1-9-3-faster-loading-times-require-4927.html
And the patch: https://gist.github.com/1008945

Thank you Xavier for providing a solution which seems to have exerted just enough pressure to get something (even if a different approach) done. My load time was cut by 52.3% using that patch. Also, I used the patch in the link with the instructions in this blog post to make it all work - thanks!

For those having trouble or not seeing performance pick up, one alternative to patching ruby is to run the rrails server which keeps the loaded rails environment so its not loaded more than once. I wrote an answer on SO explaining it http://stackoverflow.com/a/11479744/462002 .

Hungry for more? Try these related posts, or find more in the archives.