Toxic Elephant

Don't bury it in your back yard!

Try to avoid try

Posted by matijs 28/07/2015 at 10h52

Because of a pull request I was working on, I had cause to benchmark activesupport’s #try. Here’s the code:

require 'benchmark'
require 'active_support/core_ext/object/try'

class Bar
  def foo

  end
end

class Foo

end

bar = Bar.new
foo = Foo.new

n = 1000000
Benchmark.bmbm(15) do |x|
  x.report('straight') { n.times { bar.foo } }
  x.report('try - success') { n.times { bar.try(:foo) } }
  x.report('try - failure') { n.times { foo.try(:foo) } }
  x.report('try on nil') { n.times { nil.try(:foo) } }
end

Here is a sample run:

Rehearsal ---------------------------------------------------
straight          0.150000   0.000000   0.150000 (  0.147271)
try - success     0.760000   0.000000   0.760000 (  0.762529)
try - failure     0.410000   0.000000   0.410000 (  0.413914)
try on nil        0.210000   0.000000   0.210000 (  0.207706)
------------------------------------------ total: 1.530000sec

                      user     system      total        real
straight          0.140000   0.000000   0.140000 (  0.143235)
try - success     0.740000   0.000000   0.740000 (  0.742058)
try - failure     0.380000   0.000000   0.380000 (  0.379819)
try on nil        0.210000   0.000000   0.210000 (  0.207489)

Obviously, calling the method directly is much faster. I often see #try used defensively, without any reason warrented by the logic of the application. This makes the code harder to follow, and now this benchmark shows that this kind of cargo-culting can actually harm performance of the application in the long run.

Some more odd things stand out:

  • Succesful #try is slower than failed try plus a straight call. This is because #try actually does some checks and then calls #try! which does one of the checks all over again.
  • Calling #try on nil is slower than calling a nearly identical empty method on foo. I don’t really have an explanation for this, but it may have something to do with the fact that nil is a special built-in class that may have different logic for method lookup.

Bottom line: #try is pretty slow because it needs to do a lot of checking before actually calling the tried method. Try to avoid it if possible.

Tags , , no comments no trackbacks

Comments

Trackbacks

Use the following link to trackback from your own site:
http://www.matijs.net/blog/trackbacks?article_id=3551

Comments are disabled