Rubyのcatch句のコストはそこまで安くない
制御構造を適当にやっつけで作るときに(たまに)便利なcatch句のコストは、あまり安いわけではなさそうだ。
以下のベンチマークが妥当かどうかは別として、catchじゃなくてもできる処理ならば、catchを書かずに実現した方がたぶん早い。
ほとんどthrowする必要がない(けどたまに必要になるかもしれない)ようなケースでは、ifで書けるならifで書いた方がよさそうだ。
(そもそも、catch構文は複雑な制御構造からスマートに抜け出すためのものだと思っているので、使い道が違うと言われればそれまでだが)
ベンチマークの処理は、1,000,000回インクリメントするだけの処理。
これをcatch句で囲む(んで何もthrowしない)場合と比較する。倍ぐらい違う。
throwの方についても、カウンタが奇数だったらthrowする(その後に何も処理を書いていないので書かなくても同じだが)場合と、カウンタが奇数だったらnextで次のループに抜ける(throwと同じ結果になる)場合を比較する。少し見当違いなベンチマークになっている気がするが気にしない。これだと、throwするコストがさらに増えて4倍ぐらいになる。
$ ruby ./catchtest.rb RUBY_VERSION: 1.9.2 loop count: 1000000 without catch block... => 0.148608917 with catch block... => 0.296118051 with catch block, throw when the number is odd... => 0.595503125 without catch block, call next when the number is odd... => 0.142759629
胡散臭いベンチマークコード。
num = 1_000_000 counter = 0 puts "RUBY_VERSION: #{RUBY_VERSION}" puts "loop count: #{num}" puts puts "without catch block..." bef=Time.now num.times do counter += 1 end puts "=> #{Time.now - bef}" puts puts "with catch block..." bef=Time.now num.times do catch(:test_catch) do counter += 1 end end puts "=> #{Time.now - bef}" puts puts "with catch block, throw when the number is odd..." bef=Time.now num.times do catch(:test_catch) do counter += 1 throw :test_catch if (counter % 2 == 1) end end puts "=> #{Time.now - bef}" puts puts "without catch block, call next when the number is odd..." bef=Time.now num.times do counter += 1 next if (counter % 2 == 1) end puts "=> #{Time.now - bef}"
とりあえず、めんどくさいからって適当にcatchを書くのはやめようと思った。