The price of a rescue
Thursday, August 31st, 2006I’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.