Benchmarking Ruby exception handling

Which is better, handling an exception or explicitly checking to see whether or not your code is going to break? The answer is "it depends". On the one hand exception handling allows us to write more legible code (often summed up by the saying "it's easier to ask forgiveness than permission"). On the other, handling an exception is often a costly operation; it can be faster to "look before you leap".

But just how costly is exception handling? Happily it's very easy to find out. I was working away with my pair recently when this very question came up. We were calculating a ratio between two numbers, but wanted to modify the calculation so that if the denominator was 0, we'd use 0.5 instead. Our function looked something like this:

def calculate_ratio(apples, oranges) apples / oranges rescue ZeroDivisionError apples / 0.5 end

It's succinct and, I think, very readable. We call this function a lot, and in a large percentage of those calls oranges will be 0. Just how expensive is that? There's only one way to find out. Test it!

The Benchmark module contains a few nifty functions for testing this kind of scenario. I wrote two functions, ask_forgiveness and ask_permission and ran each of them a few million times with the bm() function.

require "benchmark" include Benchmark LOOP_COUNT = 1000000 def ask_forgiveness(a) 1 / a rescue ZeroDivisionError 1 / 0.5 end def ask_permission(a) denominator = (a == 0) ? 0.5 : a 1 / denominator end bm(5) do |test| test.report("Forgiveness:") do LOOP_COUNT.times do |i| ask_forgiveness(i % 2) end end test.report(" Permission:") do LOOP_COUNT.times do |i| ask_permission(i % 2) end end end

We assumed that 50% of our function calls would result in a ZeroDivisionError, which is why we're passing i % 2 into our two functions (it alternates between 0 and 1).

And the results?

user system total real Forgiveness: 6.780000 0.360000 7.140000 ( 7.172876) Permission: 1.080000 0.010000 1.090000 ( 1.090219)

A clear win (in this case) for checking ahead of time. It was worth benchmarking as I'd normally go with the more legible exception handling approach every time.

Exception handling is at it's best when exceptions really are the exception to the rule...

I love feedback and questions — please get in touch on Twitter or leave a comment.