PHPの2次元配列のメモリ使用量
今日はPHPの2次元配列がどのくらいメモリを使うかの巻。
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 ・ ・ ・ 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19
このcsvを2次元配列に展開するような状況を想定したコードを実行すると
(下記は便宜上csvと同等のデータを同時に作ってます)
<?php // filename: array_mem.php $list = []; $csv = ''; $num = 20000; $array_length = 20; foreach(range(0, $num-1) as $idx=>$current ){ $item = range(0,$array_length-1); $list[] = $item; $csv .= implode(',', $item).PHP_EOL; } $mem = number_format(memory_get_usage()); $mem_real = number_format(memory_get_usage(true)); echo '$num : '.$num. ' , $array_length : '.$array_length.' , $num * $array_length : '. number_format($num * $array_length). PHP_EOL; echo '$mem : '.$mem.PHP_EOL; echo '$mem_real : '.$mem_real.PHP_EOL; echo '$csv : '. number_format(mb_strlen($csv, 'ASCII')).PHP_EOL;
$ php -d memory_limit=-1 array_mem.php $num : 20000 , $array_length : 20 , $num * $array_length : 400,000 $mem : 65,813,688 $mem_real : 66,584,576 $csv : 1,000,000
使用量がもとのcsvの約1MBに対して約65MBメモリ食います。orz
昔、元のcsvのサイズの2倍くらいオーバーヘッドあっても大丈夫だろうとおもってこんな感じのコードを書いたらみごとにmemory exhaustedしたのを思い出したのでメモ。
でもですね…
ここまで書いたもののcsvの容量のぞくと64MBっていう、
なんだか意味ありげな数字だったんで
もしやと思って次のようにコード変えて$value_sizeを変えてくと、
どうも1023あたりがもっともメモリ効率がいいらしく、
それより少なければ少ないほど無駄にメモリを確保されてました。
大きい場合は全体サイズが大きくなっていくので、
だんだんオーバーヘッドが相対的に小さくなっている模様。
おそらくintのときは8バイト確保しているせいかとはおもいますが、
stringのときは1023+\0まで確保されているっぽい?
csvとして1KB近い値っていうのは実用的に少し考えづらいので、
いずれにせよメモリ効率は良くないですね…
時間のあるときにphpのソースの配列のところの処理を追ってみたいところですね。
ちなみにphpのバージョンは5.5.14 (64bit版)でした。
以下検証コードと結果です。
<?php // filename: array_mem.php $list = []; $csv = ''; $num = 20000; $array_length = 20; $value_size = 1023; foreach(range(0, $num-1) as $idx=>$current ){ //$item = range(0,$array_length-1); foreach(range(0,$array_length-1) as $k=>$v){ $item[$k] = str_repeat('z',$value_size); } $list[] = $item; $csv .= implode(',', $item).PHP_EOL; } $mem = memory_get_usage(); $mem_real = memory_get_usage(true); $csv_size = mb_strlen($csv, 'ASCII'); echo '$num: '.$num. ', $array_length: '.$array_length.', $value_size: '.$value_size.', $num * $array_length: '. number_format($num * $array_length). PHP_EOL; echo '$mem: '.number_format($mem).PHP_EOL; echo '$mem_real: '.number_format($mem_real).PHP_EOL; echo '$csv: '. number_format($csv_size).PHP_EOL; echo '$mem/$csv_size: '. round($mem/$csv_size,2).PHP_EOL;
$value_size=1
php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 1, $num * $array_length: 400,000 $mem: 78,417,952 $mem_real: 81,788,928 $csv: 800,000 $mem/$csv_size: 98.02
$value_size=31
$ php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 31, $num * $array_length: 400,000 $mem: 96,814,856 $mem_real: 97,517,568 $csv: 12,800,000 $mem/$csv_size: 7.56
$value_size=255
$ php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 255, $num * $array_length: 400,000 $mem: 276,019,760 $mem_real: 276,824,064 $csv: 102,400,000 $mem/$csv_size: 2.7
$value_size=511
$ php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 511, $num * $array_length: 400,000 $mem: 480,818,376 $mem_real: 481,820,672 $csv: 204,800,000 $mem/$csv_size: 2.35
$value_size=1022
$ php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 1022, $num * $array_length: 400,000 $mem: 890,034,256 $mem_real: 890,765,312 $csv: 409,200,000
$value_size=1023
$ php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 1023, $num * $array_length: 400,000 $mem: 890,434,256 $mem_real: 891,289,600 $csv: 409,600,000 $mem/$csv_size: 2.17
$value_size=1024
$ php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 1024, $num * $array_length: 400,000 $mem: 894,024,272 $mem_real: 895,221,760 $csv: 410,000,000 $mem/$csv_size: 2.18
$value_size=2047
$ php -d memory_limit=-1 array_mem.php $num: 20000, $array_length: 20, $value_size: 2047, $num * $array_length: 400,000 $mem: 1,709,645,080 $mem_real: 1,710,751,744 $csv: 819,200,000 $mem/$csv_size: 2.09
// end of snippet.