わかった。他のは全て GC が 2 回なのに対して foreach を使ったときは 4 回動いてる。たぶんこれが原因だな。
なんでだろう。統計をとってみた。
# foreach.rb GC start malloc_limit = 0x007a1200 malloc_increase = 0x0007b3bd freelist = 0x00000000 newobj = 14388 freed = 4392 GC start malloc_limit = 0x8fca857b malloc_increase = 0x0003ae66 freelist = 0x00000000 newobj = 7320 freed = 2928 GC start malloc_limit = 0xdf27c882 malloc_increase = 0x0011b9d5 freelist = 0x00000000 newobj = 34880 freed = 13952 GC start malloc_limit = 0xefab32f4 malloc_increase = 0x000bea3b freelist = 0x00000000 newobj = 23252 freed = 9298 program end malloc_limit = 0xfa9ba240 malloc_increase = 0x0001f36f freelist = 0x401fc5ec newobj = 3876 # readlines.rb GC start malloc_limit = 0x007a1200 malloc_increase = 0x0010c8f7 freelist = 0x00000000 newobj = 10002 freed = 8 GC start malloc_limit = 0xffdc70c1 malloc_increase = 0x001855fa freelist = 0x00000000 newobj = 35044 freed = 0 program end malloc_limit = 0x007a1200 malloc_increase = 0x0006ac72 freelist = 0x401e78f4 newobj = 70798
単語解説
ここからわかることは以下の通り。
つまり、readlines.rb のほうが速いのはメモリ使用量が少ないからでもなく オブジェクト生成数が少ないからでもなく、オブジェクト生成パターンが 現在の Ruby のガーベージコレクタのアルゴリズムとマッチしてしまった からだと考えられる。そのパターンとは、
である。下手にちまちまとオブジェクトを回収できてしまうと GC が平坦に発生してしまう。 負荷をかけるときは容赦なく負荷をかけて、 早いうちに ruby に回収をあきらめさせたほうがよいのだ。
というのが結論だけど、どうだろう?
なんか、malloc_limit がでかすぎる? これだと閾値が 3GB ってことになるな。
いや待てよ、そもそも GC が原因であると特定していないじゃないか。 GC を切って測定してみるべきだな。
% time ruby -ramstd/nogc foreach.rb 0.30s user 0.04s system 101% cpu 0.335 total % time ruby -ramstd/nogc readlines.rb 0.30s user 0.05s system 101% cpu 0.344 total
ほとんど変わらないな。やっぱ GC が原因か。