mswin32 ならどうか。 (先日のものより、試行回数を 5 倍に増やしています)
user system total real 0.655000 0.000000 0.655000 ( 0.661834) 0.671000 0.000000 0.671000 ( 0.669683) 0.780000 0.000000 0.780000 ( 0.775580) 2.901000 0.000000 2.901000 ( 2.899961) 9.111000 0.000000 9.111000 ( 9.118487) 10.311000 0.000000 10.311000 ( 10.313085) 11.420000 0.000000 11.420000 ( 11.439372) 13.977000 0.000000 13.977000 ( 14.013315) 21.763000 0.000000 21.763000 ( 21.772048) 15.350000 0.000000 15.350000 ( 15.402681)
これが、
user system total real 0.624000 0.000000 0.624000 ( 0.620226) 0.655000 0.000000 0.655000 ( 0.656366) 0.796000 0.000000 0.796000 ( 0.788591) 0.874000 0.000000 0.874000 ( 0.882641) 1.014000 0.000000 1.014000 ( 1.015402) 1.029000 0.000000 1.029000 ( 1.047946) 1.092000 0.000000 1.092000 ( 1.086839) 1.201000 0.000000 1.201000 ( 1.203149) 1.248000 0.000000 1.248000 ( 1.237409) 1.264000 0.000000 1.264000 ( 1.264578)
圧倒的だった。
user system total real 0.060000 0.000000 0.060000 ( 0.065711) 0.080000 0.000000 0.080000 ( 0.071005) 0.070000 0.000000 0.070000 ( 0.071445) 0.100000 0.000000 0.100000 ( 0.097273) 0.110000 0.000000 0.110000 ( 0.113550) 0.120000 0.000000 0.120000 ( 0.120162) 0.120000 0.000000 0.120000 ( 0.124773) 0.120000 0.000000 0.120000 ( 0.123037) 0.130000 0.000000 0.130000 ( 0.124647) 0.120000 0.000000 0.120000 ( 0.124635)
大分頑張った!
参考:2.2.0 trunk
user system total real 0.070000 0.000000 0.070000 ( 0.064943) 0.060000 0.000000 0.060000 ( 0.066670) 0.070000 0.000000 0.070000 ( 0.072237) 0.250000 0.000000 0.250000 ( 0.253850) 1.020000 0.000000 1.020000 ( 1.022563) 1.170000 0.000000 1.170000 ( 1.163268) 1.290000 0.010000 1.300000 ( 1.297507) 1.450000 0.000000 1.450000 ( 1.450783) 1.590000 0.000000 1.590000 ( 1.586544) 1.710000 0.000000 1.710000 ( 1.715250)
これは10倍速くなったと言っていいんじゃないか。
まぁ、だいぶインチキが混じっているのですが。でも、ソコソコ効くんじゃ無いだろうか。どんなインチキかというと、キーワードパラメータが nil の時とかに速くなる、という。あとは、どんなのが指定されることが多いんだろうな。文字列くらいサポートしてあげたいような。
先日の記事で、キーワード引数を使うと遅い、という話を書きました(http://www.atdot.net/~ko1/diary/201410.html#d11)。
ちょっと環境が変わったので、改めて測ります。
# ruby 2.2.0dev (2014-10-14 trunk 47859) [x86_64-linux] user system total real 0.070000 0.000000 0.070000 ( 0.065488) 0.060000 0.000000 0.060000 ( 0.065363) 0.070000 0.000000 0.070000 ( 0.072903) 0.270000 0.000000 0.270000 ( 0.266412) 1.040000 0.000000 1.040000 ( 1.038621) 1.200000 0.000000 1.200000 ( 1.201818) 1.370000 0.000000 1.370000 ( 1.376191) 1.430000 0.000000 1.430000 ( 1.423747) 1.540000 0.000000 1.540000 ( 1.549852) 1.730000 0.000000 1.730000 ( 1.724549)
最初(無引数)と最後(kwarg を 6 個渡す)を比べると、1.73 / 0.07 = 24 倍遅い。
で、これをなんとか速くならんかな、と思って色々やった結果がこれです。
user system total real 0.070000 0.000000 0.070000 ( 0.071088) 0.060000 0.000000 0.060000 ( 0.066202) 0.080000 0.000000 0.080000 ( 0.074154) 0.230000 0.000000 0.230000 ( 0.233521) 0.300000 0.000000 0.300000 ( 0.299151) 0.350000 0.000000 0.350000 ( 0.353437) 0.410000 0.000000 0.410000 ( 0.409449) 0.460000 0.000000 0.460000 ( 0.455629) 0.490000 0.000000 0.490000 ( 0.494256) 0.530000 0.000000 0.530000 ( 0.535178)
最初と最後を比べると、0.53 / 0.07 = 7.57 倍遅い。
遅さが 24 / 7.57 = 3 倍、改善しました。
うーん、もっと速くなると思ったんだけどな。
というわけで、Ruby 2.2 は、キーワード引数によるメソッド呼び出しが速くなるかもしれません。
その代わり、他の箇所が遅くなっている可能性が...。
しかし、どこが遅いんだろうなぁ。実は、4番目(foo_kw6 に無引数で渡す)が、もっと速くなると踏んでいたんだけどな。Hash#key? の呼び出しって速いのか?
ちょっとだけ弄ってみた。
user system total real 0.070000 0.000000 0.070000 ( 0.067622) 0.070000 0.000000 0.070000 ( 0.069523) 0.070000 0.000000 0.070000 ( 0.073671) 0.230000 0.000000 0.230000 ( 0.231982) 0.310000 0.000000 0.310000 ( 0.302289) 0.350000 0.000000 0.350000 ( 0.352416) 0.420000 0.000000 0.420000 ( 0.423110) 0.470000 0.000000 0.470000 ( 0.469080) 0.500000 0.000000 0.500000 ( 0.499148) 0.550000 0.000000 0.550000 ( 0.550228)
むしろ遅くなってる! うーん。
もうちょっと頑張ったが、
user system total real 0.070000 0.000000 0.070000 ( 0.066390) 0.060000 0.000000 0.060000 ( 0.069617) 0.080000 0.000000 0.080000 ( 0.075847) 0.260000 0.000000 0.260000 ( 0.258485) 0.310000 0.000000 0.310000 ( 0.313561) 0.360000 0.000000 0.360000 ( 0.357482) 0.410000 0.000000 0.410000 ( 0.415190) 0.460000 0.000000 0.460000 ( 0.460494) 0.480000 0.000000 0.480000 ( 0.485287) 0.560000 0.000000 0.560000 ( 0.559535)
あんまり変わんないな。うーん。
いつの間にか 10 月ですよ...。
Ruby 2.0 から導入されたキーワード引数の性能評価。
require 'benchmark' def foo0 end def foo3 a, b, c end def foo6 a, b, c, d, e, f end def foo_kw6 k1: nil, k2: nil, k3: nil, k4: nil, k5: nil, k6: nil end N = 1_000_000 Benchmark.bm{|x| x.report{ N.times{ foo0 } } x.report{ N.times{ foo3 1, 2, 3 } } x.report{ N.times{ foo6 1, 2, 3, 4, 5, 6 } } x.report{ N.times{ foo_kw6 } } x.report{ N.times{ foo_kw6 k1: 1 } } x.report{ N.times{ foo_kw6 k1: 1, k2: 2 } } x.report{ N.times{ foo_kw6 k1: 1, k2: 2, k3: 3 } } x.report{ N.times{ foo_kw6 k1: 1, k2: 2, k3: 3, k4: 4 } } x.report{ N.times{ foo_kw6 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5 } } x.report{ N.times{ foo_kw6 k1: 1, k2: 2, k3: 3, k4: 4, k5: 5, k6: 6 } } }
何をしているかというと、
と、いうのが、どんな性能特性を持つか調べたいと思います。
というか、結果。
ruby 2.2.0dev (2014-10-10 trunk 47867) [i386-mswin32_110] user system total real 0.140000 0.000000 0.140000 ( 0.134481) 0.141000 0.000000 0.141000 ( 0.140427) 0.171000 0.000000 0.171000 ( 0.180837) 0.593000 0.000000 0.593000 ( 0.595162) 1.778000 0.016000 1.794000 ( 1.787873) 2.028000 0.000000 2.028000 ( 2.034146) 2.247000 0.000000 2.247000 ( 2.255171) 2.464000 0.000000 2.464000 ( 2.470283) 2.621000 0.000000 2.621000 ( 2.639155) 2.855000 0.000000 2.855000 ( 2.863643)
キーワード引数のほうは、キーワードの数に大分比例している感じですね。これは、多分、呼び出し側で Hash 作ってるんだけど、その生成コストなんじゃないかな、って思う。
6引数のメソッドで渡す場合(0.17 秒)に比べて、6個キーワード引数渡すと、2.85 秒と、10倍以上遅いことがわかります。
続きます。
おまけ
def foo a, b, k1: eval('kr.dup'), k2: 2, **kr p k1 end foo 1, 2, k2: 'v2', k3: 'v3'
何が表示されるか。
最後の **kr では、keyword 引数で指定しなかった {k3: 'v3'} というものが得られるはずだが、実装の都合で k1 のデフォルト引数を評価中は、まだ k2 が残ってしまうので、{:k2=>"v2", :k3=>"v3"} が見えてしまう、という。dup しないと、その時の snapshot が見えない。