K.Sasada's Home Page

Diary - 2014 October

研究日記

神無月

_16(Thu)

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)

圧倒的だった。

_15(Wed)

       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 の時とかに速くなる、という。あとは、どんなのが指定されることが多いんだろうな。文字列くらいサポートしてあげたいような。

_14(Tue)

先日の記事で、キーワード引数を使うと遅い、という話を書きました(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)

あんまり変わんないな。うーん。

_11(Sat)

いつの間にか 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
    }
  }
}

何をしているかというと、

  • 0 引数のメソッド呼び出し
  • 3 引数のメソッド呼び出し
  • 6 引数のメソッド呼び出し
  • 6 個のキーワードを持つメソッドに 0 個キーワードを渡して実行
  • 6 個のキーワードを持つメソッドに 1 個キーワードを渡して実行
  • 6 個のキーワードを持つメソッドに 2 個キーワードを渡して実行
  • 6 個のキーワードを持つメソッドに 3 個キーワードを渡して実行
  • 6 個のキーワードを持つメソッドに 4 個キーワードを渡して実行
  • 6 個のキーワードを持つメソッドに 5 個キーワードを渡して実行
  • 6 個のキーワードを持つメソッドに 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 が見えない。

Sasada Koichi / ko1 at atdot dot net
$Date: 2003/04/28 10:27:51 $