Archive for August, 2006

The price of a rescue

Thursday, August 31st, 2006

I’ve been abusing Ruby exception handling lately. Instead of doing something like:

if user and user.group
  user.group.do_something
end

or a cleaner

user and user.group and user.group.do_something

I just do

user.group.do_something rescue nil

And let Ruby deal with the consequences of a missing link.

It was all nice and pretty, until I saw this little guy standing in my shoulder — dressed in white and with a halo over his head — telling me that laziness is wrong and that there would be a price to pay. After all, exception handling requires setting up flags and pointers and blocks and contexts and who knows what else. They must be expensive. And of course, there was another guy — red, with horns and a tail — calling the white guy an asshole and a coward.

In the end I decided to stop taking drugs, and to do my own research into the subject, instead of paying attention to my own hallucinations.

I wrote the simplest test cases, and used the ‘benchmark’ library to measure their performance:

require 'benchmark'

n = 500000
Benchmark.bm(7) do |x|
  x.report("plain") do
    for i in 1..n
      1.0/5.0
    end
  end

  x.report("safe") do
    for i in 1..n
      begin
        1.0/5.0
      rescue
        0.2
      end
    end
  end

  x.report("rescue") do
    for i in 1..n
      begin
        1.0/0.0
      rescue
        0.2
      end
    end
  end
end

It does half-a-million floating point divisions. The first scenario (”plain”) has no exception handling. The second one (”safe”) has exception handling, but never raises an exception. And the final scenario (”rescue”) uses exception handling all the time, triggered by a division by zero.

The results are something like this:

             user     system      total        real
plain    0.780000   0.010000   0.790000 (  1.769231)
safe     0.910000   0.020000   0.930000 (  1.781405)
rescue   0.900000   0.010000   0.910000 (  1.945978)

I know Zed will probably kill me for making any conclusions based on such a small sample (the half-a-million divisions are not the sample size… the sample size is one single run of the test). But the numbers are pretty much the same when you run the tests several times.

Using rescue is not much more expensive than running naked. In my tests in particular, it never was more than 5% slower. It might even be cheaper than multiple tests.

It’s fast enough for me not to worry about it, specially considering the fact that my own operations are probably going to be a lot slower than a simple division, thus diluting any slowdown even further.

If you really care about this, then by all means make your own tests and take your own conclusions. Otherwise, just trust the guy dressed in red, with the horns and the pointy tail: use and abuse rescue until you’re tired of it; it’s not worth worrying about its performance impact.