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 にはほとんど対応できていないオンボードビデオなんだと思う。
[ ツッコむ ]
以上です。