You probably shouldn’t do this, but it works really well

I just added the following config to a ruby test suite:

RSpec.configure do |config|
  config.before :each do
    GC.disable
  end
 
  config.after :each do
    GC.enable
  end
end

It’s pretty tragic how well this works. It knocked about 40% off the runtime of the test suite for our Padrino + DataMapper app running on 1.8.7 (yes, I know. I’m sorry. I actually discovered this as part of my work on getting it off 1.8.7), and a friend is reporting a more than 50% drop for his Rails app running on 1.9.3.

Why? Well, I added this as an experiment when I noticed how much time the test suite was spending in the GC (I initially ran with GC turned off entirely. This ate about as much memory as you’d expect). I’m assuming it’s a combination of MRI’s GC being really quite shit and the fact that test setup tends to produce a lot of garbage.

This is kinda an awful solution to this problem, but I must admit I’m finding it hard to argue with the results. If you can afford to throw memory at the problem it might actually be worth doing.

But still… ouch.

This entry was posted in Code on by .

9 thoughts on “You probably shouldn’t do this, but it works really well

    1. david Post author

      No, I can’t unfortunately. This app is actually stuck on 1.8.7 and I discovered this hack as part of the attempt to upgrade it to a more recent version, but the rails app with the 50% drop is on 1.9.3.

      It’s entirely possible this problem goes away in 2.0, but I’ve no way to verify it.

    1. david Post author

      Huh. Well, good to know it doesn’t work for everything. And somewhat reassuring. Sorry you didn’t get the magic speedup I promised though!

  1. Matt Valentine-House

    I get similar results by tuning some GC parameters in my bashrc. This results in significantly fewer GC runs (and the associated memory bloat) but feels a little safer than turning GC off entirely.


    export RUBY_HEAP_MIN_SLOTS=1000000
    export RUBY_HEAP_SLOTS_INCREMENT=1000000
    export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
    export RUBY_GC_MALLOC_LIMIT=100000000
    export RUBY_HEAP_FREE_MIN=500000

    running with GC disabled my suite runtime is 38.5 s compared to 39.9 with the above settings. Some more good information is here: http://stackoverflow.com/questions/4985310/garbage-collector-tuning-in-ruby-1-9

  2. John Bintz

    This isn’t for everything, but in certain apps that thrash memory during tests I sometimes do a similar, but more drastic, setup. On test start I set a variable with the current time. After leaving the spec/feature, if ~2 seconds have passed, I clear the GC and update the variable with the current time. It eats way more RAM than your approach and invokes the GC a lot less often, but since it’s the last step after optimizing/tuning/cleaning up the rest of the app/test suite, if it happens to shave some time off of the tests, and only takes 10 lines of code to invoke, I’ll do it.

  3. Chris S.

    Yep, can confirm that 1.9.3 does not really benefit from this. Yay?

    With GC disabled (in the rspec’s before each):

    Finished in 10.73 seconds

    Normal GC:

    Finished in 10.73 seconds

    My ruby version:

    ruby --version
    ruby 1.9.3p392

Comments are closed.