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で配列を宣言する場合には、型も同時に指定する必要があるようです。
$/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
グラフにすると、メモリ容量は
となり、そのCPU時間は
となりました。
結果として、Ruby22は今回のテストでは、一レコード毎に1KB程度メモリを消費し、JRuby90は7割、Crystalは半分程度のメモリを消費するという結果になりました。ただし、JRubyはオブジェクトがほとんどない状態でもメモリ使用量が多いため、今回のテストではRuby22との逆転は見られません。
配列を増やす速度については、JRuby90とCrystalが同じ位で、Ruby22はその5倍程度遅いという結果でした。
個人的には、思ったより差は出ないんだなという印象です。