やはり 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'

でモジュールのインストールは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 => " " });

結果は次のようになりました。
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

ここで分かったことは、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プログラミング
スリラム スリニバサン Sriram Srinivasan 須田 隆久
オライリー・ジャパン (1998/11)
売り上げランキング: 148,052
通常3〜4日以内に発送
- スポンサーリンク -