やはり Perl はメモリ喰いな言語。データ型の内部構造
「Perl 5.6系 と 5.8系では巨大配列の処理効率が違う」でも書いたのですが、巨大な配列データの扱い方でメモリ使用量でハマってます。そもそも csv ファイルで 100MByte 程度のデータが Perl 内部では 1GByte 程度もメモリを喰うのが納得いかない。
そこで、Perl のメモリ使用量を調査してみるととにしました。必要となるモジュールは、
Devel::Size と Devel::Size::Report です。
perl -MCPAN -e 'install Devel::Size'
perl -MCPAN -e 'install Devel::Size::Report'
perl -MCPAN -e 'install Devel::Size::Report'
でモジュールのインストールはOK。
さて、メモリ使用量の調査開始です。単純なものにターゲットを絞って、整数型と文字列型のメモリ使用量を調べてみました。
- スポンサーリンク -
use Devel::Size::Report qw/report_size/;
# 1bit, 1byte, 2byte, 3byte, 4byte(=32bit), 5byte
my $b = [ 0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF, 0xFFFFFFFFFF ];
print report_size($b, { indent => " " });
# null, length=1, 2, 3, 4, 5
my $c = [ '', '1', '10', '100', '1000', '10000' ];
print report_size($c, { indent => " " });
# 1bit, 1byte, 2byte, 3byte, 4byte(=32bit), 5byte
my $b = [ 0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF, 0xFFFFFFFFFF ];
print report_size($b, { indent => " " });
# null, length=1, 2, 3, 4, 5
my $c = [ '', '1', '10', '100', '1000', '10000' ];
print report_size($c, { indent => " " });
結果は次のようになりました。
Size report v0.10 for 'ARRAY(0x8c8bc28)':
Array ref 192 bytes (overhead: 92 bytes, 47.92%)
Scalar 16 bytes
Scalar 16 bytes
Scalar 16 bytes
Scalar 16 bytes
Scalar 16 bytes
Scalar 20 bytes
Total: 192 bytes in 7 elements
Size report v0.10 for 'ARRAY(0x8c9a010)':
Array ref 257 bytes (overhead: 92 bytes, 35.80%)
Scalar 25 bytes
Scalar 26 bytes
Scalar 27 bytes
Scalar 28 bytes
Scalar 29 bytes
Scalar 30 bytes
Total: 257 bytes in 7 elements
Array ref 192 bytes (overhead: 92 bytes, 47.92%)
Scalar 16 bytes
Scalar 16 bytes
Scalar 16 bytes
Scalar 16 bytes
Scalar 16 bytes
Scalar 20 bytes
Total: 192 bytes in 7 elements
Size report v0.10 for 'ARRAY(0x8c9a010)':
Array ref 257 bytes (overhead: 92 bytes, 35.80%)
Scalar 25 bytes
Scalar 26 bytes
Scalar 27 bytes
Scalar 28 bytes
Scalar 29 bytes
Scalar 30 bytes
Total: 257 bytes in 7 elements
ここで分かったことは、SCALAR 型のデータは、Integer 型である場合、最小で16byte のメモリを必要とする。以後、32bit 単位で 4byte 増加する。String 型の場合、最小で25 byte のメモリを必要とし、以後 1 ASCII につき 1byte 必要とする。
つまりは、C 言語等のメモリを自前で確保する言語と比較して、莫大なオーバーヘッドが必要となるわけです。たとえば、
32bit 整数型データが 5万レコード × 200 セルの場合、本来であれば、
* 4byte × 50,000 × 200 = 40Mbyte
ですむところが、perl では整数型で取り込むと、
* 16byte × 50,000 × 200 = 160Mbyte
も必要とする。もしも文字型で代入していたら、
* 26byte × 50,000 × 200 = 260Mbyte
も必要としてしまうのです。だんだん計算が合ってきました。csv で100Mbyte 程度のデータを全て文字として代入しているので、100Mbyte × 4 倍程度 = 400Mbyte も必要とする。DBI から帰ってくるデータサイズを計測すると、そのくらいでした。
う〜ん・・・perl で大量のデータを扱うのは XS で書かないと無理ですねぇ〜
ちなみに、この書籍の最終章「Perl データ型の内部構造」が参考になります。その章でもメモリサイズについては語られていませんが、データ型がどう管理されているかを知ることができます。
実用Perlプログラミング
posted with amazlet at 05.04.21
スリラム スリニバサン Sriram Srinivasan 須田 隆久
オライリー・ジャパン (1998/11)
売り上げランキング: 148,052
通常3〜4日以内に発送
オライリー・ジャパン (1998/11)
売り上げランキング: 148,052
通常3〜4日以内に発送
- スポンサーリンク -
コメントやシェアをお願いします!
drk
miyagawaさま>実はPacked::Arrayを見つけていたのですが、Integer専用だったので使うのをヤメました。本当の文字列データも大量に含んでいまして。
無事に別の方法で解決しました。
coolでないのですが、いったん文字列結合してメモリ内で管理して、必要時に適宜分解したりしました。
miyagawa
Packed::Array を使うというのは?
http://search.cpan.org/~dsugal/Packed-Array-0.01/