読者です 読者をやめる 読者になる 読者になる

かってにインパクトファクター

子育てサラリーマンが日々の雑多なことをつらつらと綴ってます。時々政治ネタ経済ネタコンピュータネタなどをはさみます。

Ruby22, JRuby90, Crystal で配列を作った時にどれくらいメモリを消費するのか調べた

Rubyを使ってプログラムを書くと、大量に配列を作った時にメモリをどれくらい消費しているのか気になったので調べてみました。
本当は、配列の代わりにハッシュを使った場合や、2次元配列なども調べたかったのですが、それは後日ということで、今日は単純な配列を作った場合に使用するメモリの比較です。

前からこの手のテストをしたいとは思っていたんですが、これまで良いツールが見つからず、着手出来なかったんです。最近、GNUのTIMEコマンドなら出来るということを知りまして、このツールを使って結果を調べてみました。

ソースは、
memtest1.rb

a = []
ARGV[0].to_i.times{|i|
  a.push( (0 .. 10).map{ ('A' .. 'Z' ).to_a[(25 * rand()).to_i]}.join)
}

memtest1.cr

a = Array(String).new
ARGV[0].to_i.times{|i|
  a.push( (0 .. 10).map{ ('A' .. 'Z').to_a[ (25 * rand()).to_i]}.join)
}

ほぼ同じソースを利用していますが、配列の宣言が少し異なります。詳しくはまだ理解していませんが、Crystalで配列を宣言する場合には、型も同時に指定する必要があるようです。

実行方法ですが、rubyjrubyでは

$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 10

とし、crystalでは

$/usr/bin/time -f "%Us %M KB" crystal run memtest1.cr -- 10

としています。crystalでは、引数の前に--を付ける必要があるようです。

今回も前回同様、buildオプションをつけて実行しようとしたのですが、

$crystal build memtest1.cr
$./memtest1
Index out of bounds (IndexOutOfBounds)
*Exception@Exception#initialize<IndexOutOfBounds, String>:Array(string) +46 [0]
*IndexOutOfBounds#initialize<IndexOutOfBounds, String>:Array(String) +6 [0]
*IndexOutOfBounds::new<String>:IndexOutOfBounds +97 [0]
*IndexOutOfBounds::new:IndexOutOfBounds +16 [0]
*Array(String)@Array(T)#at<Array(String), Int32>:String +114 [0]
*Array(String)@Array(T)#[]<Array(String), Int32>:String +6 [0]
__crystal_main +22620 [0]
main +35 [0]
__libc_start_main +253 [0]
_start +41 [0]

$crystal build memtest1.cr --release
$./memtest1
Index out of bounds (IndexOutOfBounds)
main +16816 [0]
__libc_start_main +253 [0]
_start +41 [0]

となり実行できませんでした。


実行結果ですが、
Ruby22では

$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 100
0.14s 27984 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 1000
0.21s 28016 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 10000
1.68s 33072 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 100000
16.65s 115376 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 200000
31.36s 202736 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 500000
104.63s 530256 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 1000000
204.86s 1003856 KB

JRuby90では

$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 100
11.61s 350448 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 1000
13.35s 367360 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 10000
12.47s 359280 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 100000
17.66s 426704 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 200000
23.68s 527040 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 500000
38.43s 693440 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 1000000
56.02s 1056640 KB

Crystalでは

$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 100
0.40s 107472 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 1000
0.54s 107440 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 10000
0.60s 107552 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 100000
3.72s 107568 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 200000
7.39s 126768 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 500000
21.85s 232096 KB
$/usr/bin/time -f "%Us %M KB" ruby memtest1.rb 10
42.44s 458240 KB

グラフにすると、メモリ容量は
f:id:postmaster:20150624004428p:plain
となり、そのCPU時間は
f:id:postmaster:20150624004447p:plain
となりました。

結果として、Ruby22は今回のテストでは、一レコード毎に1KB程度メモリを消費し、JRuby90は7割、Crystalは半分程度のメモリを消費するという結果になりました。ただし、JRubyはオブジェクトがほとんどない状態でもメモリ使用量が多いため、今回のテストではRuby22との逆転は見られません。
配列を増やす速度については、JRuby90とCrystalが同じ位で、Ruby22はその5倍程度遅いという結果でした。
個人的には、思ったより差は出ないんだなという印象です。