Ruby では明示的に return を書くほうがちょっと遅い、は 1.9 でも有効か

Ruby ではメソッドの最後の式の値がそのまま返り値になります。どこで読んだのか忘れてしまいましたが「明示的に return を書くとちょっとだけ遅いので、不要なら明示的な return を書かずに返り値の式だけ書くのが Ruby 流」という言説があって、その当時試してみると確かに明示的に return を書かないほうがちょっと速いという結果だったので「不要なら明示的な return は書かないのがジャスティス」というのを信条にしてきました。

しかし先日その話をしていて、ふと 1.9 ではもうこの"常識"は通用しなくなってるんじゃないかと思いついたので確認してみました。

benchmark に使ったスクリプトと実行結果は gist に置きました。

https://gist.github.com/935934

明示的な return の有無と、さらに block の中から return するものと block の返す値をそのまま返す(明示的に return しない)ものを 10000000 回呼んでいます。
LinuxMac OS X で実行してみましたがだいたい同じ傾向だったので Mac OS X の結果を載せるとこんな感じでした。

ruby 1.9.3dev (2011-04-23 trunk 31323) [x86_64-darwin10.7.0]

user system total real
return なし 0.820000 0.000000 0.820000 0.818726
return あり 0.830000 0.000000 0.830000 0.825238
block 内から return 13.280000 0.030000 13.310000 13.291957
block あり(return なし) 15.630000 0.040000 15.670000 15.831282

メソッドから直接値を返す時は return の有無は差がありません。*1
それもそのはずで、実は return の有無は Ruby 1.9 の InstructionSequence にコンパイルされると違いがなくなるので、両者は実行時の動作は全く同じになっているのです。
また block 呼び出しがあると時間がかかるのはもちろんですが、block の中から return するのはやや速いというのがわかります。これはちょっと意外でした。ブロック終了の処理をすっとばしているからでしょうか。

1.8.7 での結果はこうなりました。

ruby 1.8.7 (2010-01-10 patchlevel 249) [i686-darwin10.3.0]

user system total real
return なし 5.450000 0.000000 5.450000 5.453376
return あり 6.440000 0.000000 6.440000 6.436711
block 内から return 35.040000 1.600000 36.640000 36.632049
block あり(return なし) 37.950000 1.640000 39.590000 39.597771

やはり 1.8 では明示的な return を書くほうが遅いようです。しかし 1.8 でも block 内での return は逆にやや高速です。しかし 1.9 と比較すると差は小さくなっています(1.9 のほうが block 呼び出しから返る処理のコストが高いんでしょうか)。

まとめ

  • 「明示的な return が少し遅い」というのは 1.8 についてあてはまるが 1.9 ではそんなことはない
  • block の中で return するのは(そのブロックを正常に抜けるのに較べて)むしろ速い

あたりまえですが 1.8 時代に培ったパフォーマンスについての常識は 1.9 では通用しなくなっていることが多そうですね。

[追記]そういえば使用したベンチマーク用のスクリプトではメソッド呼び出しを繰り返すのに n.times ではなくて while を使ったループを利用しています。これは n.times などのブロック呼び出しを伴なう方法だとブロック呼び出しのコストが高くてループ処理自体に時間がかかってしまうためです。「each や times より while を使うほうが高速」という"常識"は 1.9 でも健在です。[/追記]

*1:この結果ではやや明示的 return するほうが遅いですが何度か実行すると逆転することもあるので偶然です。