たらい回し関数

小飼弾さんのブログの過去記事(参照)を読んでいて、「たらい回し」関数ってのの存在を知りました。コードを読んでいて何の意味があるのだろうと思っていたら、関数呼び出しのベンチマークに使う有名な関数なのですね。Wikipedia に書いてありました。
竹内関数 - Wikipedia
発明したのは日本人だそうです。

で、結果を見てみたら、Ruby は惨憺たる結果ですな。でも、おもしろそうなので自分でもやってみました。コードと数値は弾さんのままで、Linux Mint 17.2 で実行しました。Ruby 2.2.3 です。結果は

tak(12, 6, 0) = 12

real	0m0.741s
user	0m0.712s
sys	0m0.024s

です。比べ物にならないくらい高速化されていますね。まあ、最近の PC が速いのかも知れないが、僕のノートパソコンはふつうの VAIO です。

ついでに Python 2.7 でもやってみました。これも弾さんのままです。

tak(12, 6, 0) = 12

real	0m1.336s
user	0m1.328s
sys	0m0.008s 

へえ、Ruby の方が速いですね。Ruby 頑張りました。弾さんの結果だと Python の圧勝ですが。

弾さんの Ruby コードを載せておきますね。こんな風に書くんだ。

def tak(x, y, z)
  if x <= y
  then y
  else tak(tak(x-1, y, z),
           tak(y-1, z, x),
           tak(z-1, x, y))
  end
end

x, y, z = ARGV.size == 3 ? ARGV.map{|s| s.to_i } : [10, 5, 0]
puts "tak(#{x}, #{y}, #{z}) = #{ tak(x, y, z) }";

僕ならメソッドの部分は

def tak(x, y, z)
  (x <= y) ? y : tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y))
end

などと書いてしまうでしょうな。

Ruby でも弾さんの C 並の結果が出ているので、これはやはりハードウェアの10年間の進歩が大きいのでしょうね。

ついでだ、C でもやっておこう。

tak(12, 6, 0) = 12

real	0m0.077s
user	0m0.076s
sys	0m0.000s

さすがに C だ。弾さんの結果と比較すると、C で6.8倍、Python で8.0倍、Ruby で37.9倍になっている(user で比較)ので、Ruby が頑張って高速化したのがわかります。まあ、以前は遅すぎたのだな。