PHP で 大きな配列 を
扱う際に気をつけること
2015-07-28 GMOリサーチ 寺田渉
facebook: 寺田渉
github: waterada
twitter: @wa_terada
自己紹介 (会社)
- PHP (CakePHP) / java (Spring MVC) を主に使って開発
- 継続的インテグレーション
- github + git flow で運用
- PHPUnit / JUnit で カバレッジ 100%
- Behat (Selenium Driver 経由の画面テスト) 利用
- vagrant で開発環境構築
自己紹介 (趣味)
CakePHP 公式ドキュメント 翻訳
自己紹介 (趣味)
ボードゲーム 翻訳
自己紹介 (趣味)
TED 翻訳
自己紹介
プログラミング & 翻訳
大好き人間です
大きな配列 を持つのに
どれ程の メモリ が必要か。
で、本題。
皆さん、
気になったことはありませんか?
1桁の数値×5,000,000個の値を
配列として一旦 メモリに保持したい。
メモリがどれくらいあれば
足りる?
たとえば
測ってみました
まずは1万個まで
# CSV 10~10,000
for N in `perl -e 'print join(" ",1..1000)'`; do php -r '
$a = memory_get_usage();
$b = implode(",", range(1,'$N'0));
echo "'$N'0\t" . (memory_get_usage() - $a) . "\n";
'; done > result_10_to_10k.csv.txt
# 配列 10~10,000
for N in `perl -e 'print join(" ",1..1000)'`; do php -r '
$a = memory_get_usage();
$b = range(1,'$N'0);
echo "'$N'0\t".(memory_get_usage()-$a)."\n";
'; done > result_10_to_10k.array.txt
# 変数 10~10,000
for N in `perl -e 'print join(" ",1..1000)'`; do php -r '
$a = memory_get_usage();
for ($i = 0; $i < '$N'0; $i++) { ${"b" . $i} = 1; }
unset($i);
echo "'$N'0\t".(memory_get_usage()-$a)."\n";
'; done > result_10_to_10k.vars.txt
参考(確認に使ったスクリプト):
# CSV 100k~5000k
for N in `perl -e 'print join(" ",1..50)'`; do php -r '
$a = memory_get_usage();
$b = implode(",", range(1,'$N'00000));
echo "'$N'00000\t" . (memory_get_usage() - $a) . "\n";
'; done > result_100k_to_5000k.csv.txt
# 配列 100k~5000k
for N in `perl -e 'print join(" ",1..50)'`; do php -r '
$a = memory_get_usage();
$b = range(1, '$N'00000);
echo "'$N'00000\t" . (memory_get_usage() - $a) . "\n";
'; done > result_100k_to_5000k.array.txt
# 変数 100k~5000k
for N in `perl -e 'print join(" ",1..50)'`; do php -r '
$a = memory_get_usage();
for ($i = 0; $i < '$N'00000; $i++) { ${"b" . $i} = 1; }
unset($i);
echo "'$N'00000\t" . (memory_get_usage() - $a) . "\n";
'; done > result_100k_to_5000k.vars.txt
参考(確認に使ったスクリプト):
1要素あたり 最低でも
およそ
150~200 バイト
必要
ということは
500万個 の配列に
およそ 750MB が必要
750 メガバイト !!
皆さん、大きい配列には
気をつけましょうね。
おまけ
500万の数列を
省メモリで持てるクラス作ってみた
//0~500万までの数値を持つ配列作る
$array = new MemorySafeArray();
for ($i = 0; $i < 5000000; $i++) {
$array->push($i);
}
//メモリに保持した配列をループして出力する
foreach ($array->toIterator() as $item) {
echo $item . "\n";
}
こんなクラス
class MemorySafeArray {
private $__compressed = "";
public function __construct($items = []) {
$this->__compressed = "";
$this->pushAll($items);
}
public function push($item) {
$this->__compressed .= $item . "\0";
}
public function pushAll($array) {
foreach ($array as $item) {
$this->push($item);
}
}
public function toIterator() {
$pos1 = 0;
while (($pos2 = strpos($this->__compressed, "\0", $pos1)) !== false) {
yield substr($this->__compressed, $pos1, $pos2 - $pos1);
$pos1 = $pos2 + 1;
}
}
}
実際にはこんなコードです
以上です!
感想などあれば:
facebook: 寺田渉
github: waterada
twitter: @wa_terada
ご静聴ありがとうございました!
PHP で大きな配列扱う際に気をつけること
By Wataru Terada
PHP で大きな配列扱う際に気をつけること
- 4,639