2011/10/03(月) [n年前の日記]
#2 [prog] Ruby の case when とメソッドテーブルについて調べたり
Ruby の解説ページをググっていたら、case when は if文に変換されるという話を見かけて、処理時間が気になってきたわけで。テーブルジャンプにしたいけど書き方が分からない。が、ダメ元で某2chで質問してみたら、書き方についてレスがいただけたので、感謝しつつ引き続き調査。
以下のようなスクリプトを書いてベンチマークを取ってみた。
Core2Duo E8400 3.0GHz + Windows7 Pro 64bit + ActiveScriptRuby 1.8.7 p330 での実行結果は以下の通り。
が、某2chで、「1.9はテーブルジャンプに置き換えてくれるはずだ」という話が。
ActiveScriptRuby 1.9.2 p290 で試してみたところ、以下の結果に。
ここまで速くなるとは知らなかった。Ruby 1.9 の最適化?は素晴らしいなと。
ということで結論。書き方次第で速くなるかも、などとグダグダ悩むぐらいなら、どうにかして Ruby 1.9 で動かせないかを悩んだほうがはるかに効率がいい。
でも、exerb って Ruby 1.9 には対応してなかったような記憶が。
以前、手持ちのネットブック機(Atom CPU)上で、exerb と ocra でexe化したスクリプトを動かしたら、exerb版に比べてocra版は、起動するまでの時間が数倍かかってゲンナリした記憶があり。なので、最終的に exe化する可能性があるなら、極力 Ruby 1.8.x でやるべきかな、と思っていたわけで。どうしたもんか。
まあ、そのネットブック上で DXRuby のスクリプトを動かすと、メイン機上では軽々と60FPS出ていたスクリプトが、6〜10FPS程度になったりするわけで。 *1 そういう環境は無視してしまっていいのかもしれない。
以下のようなスクリプトを書いてベンチマークを取ってみた。
#!/usr/bin/ruby require "benchmark" LOOP_MAX = 100000 def func0(x) return x * 3 end def func1(x) return x * 4 end def func2(x) return x * 5 end def func3(x) return x * 6 end def func4(x) return x * 7 end def func5(x) return x * 8 end def func6(x) return x * 9 end def func7(x) return x * 10 end def func8(x) return x * 11 end def func9(x) return x * 12 end def func10(x) return x * 13 end def func11(x) return x * 14 end def func12(x) return x * 15 end def func13(x) return x * 16 end def func14(x) return x * 17 end def func15(x) return x * 18 end $a = 0 $b = 0 $fixnum = 15 puts Benchmark::CAPTION puts Benchmark.measure{ LOOP_MAX.times{ |i| # x = i % 16 x = $fixnum r = 0 case x when 0 r = x * 3 when 1 r = x * 4 when 2 r = x * 5 when 3 r = x * 6 when 4 r = x * 7 when 5 r = x * 8 when 6 r = x * 9 when 7 r = x * 10 when 8 r = x * 11 when 9 r = x * 12 when 10 r = x * 13 when 11 r = x * 14 when 12 r = x * 15 when 13 r = x * 16 when 14 r = x * 17 when 15 r = x * 18 end $a += r } } puts Benchmark.measure{ funclist = [ :func0, :func1, :func2, :func3, :func4, :func5, :func6, :func7, :func8, :func9, :func10, :func11, :func12, :func13, :func14, :func15, ] LOOP_MAX.times{ |i| # x = i % 16 x = $fixnum r = send( funclist[x], x ) $b += r } } puts "a=#{$a}" puts "b=#{$b}"
Core2Duo E8400 3.0GHz + Windows7 Pro 64bit + ActiveScriptRuby 1.8.7 p330 での実行結果は以下の通り。
user system total real 0.374000 0.000000 0.374000 ( 0.380038) 0.140000 0.000000 0.140000 ( 0.146014) a=27000000 b=27000000色々試してみたが、以下のような感じの結果に。
- たかだか16個程度の数値の振り分けでも、最悪の場合、case when のほうがメソッドテーブルより 2.7倍ぐらい遅い。
- when の数が3〜4個ぐらいで、メソッドテーブルの処理時間と同程度になる。
- つまり、when の数が3〜4個より多いなら、メソッドテーブルにしたほうが処理は速くなる。
が、某2chで、「1.9はテーブルジャンプに置き換えてくれるはずだ」という話が。
ActiveScriptRuby 1.9.2 p290 で試してみたところ、以下の結果に。
user system total real 0.031000 0.000000 0.031000 ( 0.030003) 0.031000 0.000000 0.031000 ( 0.027003) a=27000000 b=27000000ほとんど同じ処理時間。しかも、1.9.2 のほうが、1.8.7 より 5〜12倍速い。
ここまで速くなるとは知らなかった。Ruby 1.9 の最適化?は素晴らしいなと。
ということで結論。書き方次第で速くなるかも、などとグダグダ悩むぐらいなら、どうにかして Ruby 1.9 で動かせないかを悩んだほうがはるかに効率がいい。
でも、exerb って Ruby 1.9 には対応してなかったような記憶が。
以前、手持ちのネットブック機(Atom CPU)上で、exerb と ocra でexe化したスクリプトを動かしたら、exerb版に比べてocra版は、起動するまでの時間が数倍かかってゲンナリした記憶があり。なので、最終的に exe化する可能性があるなら、極力 Ruby 1.8.x でやるべきかな、と思っていたわけで。どうしたもんか。
まあ、そのネットブック上で DXRuby のスクリプトを動かすと、メイン機上では軽々と60FPS出ていたスクリプトが、6〜10FPS程度になったりするわけで。 *1 そういう環境は無視してしまっていいのかもしれない。
*1: おそらく、DirectX にはほとんど対応できていないオンボードビデオなんだと思う。
[ ツッコむ ]
以上です。