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

ご静聴ありがとうございました!

Made with Slides.com