読者です 読者をやめる 読者になる 読者になる

aspec7's garage

エンジニア生活の中で学んだことの備忘録

PHP 5.5 のGeneratorを試してみました

PHP

PHP5.5で追加されたジェネレータを試して見ました。

ただ試すだけでは何なので、メモリの消費量と処理スピードを計測する形のサンプルです。
処理としては、CSVデータをランダム値で生成して、それを配列とジェネレータで処理した場合で比較しました。

define('CSV_DELIMITER', ',');

/**
* CSVデータをランダム作成
*
* @return string CSVデータを文字列で返す
*/
function makeCsv()
{
$row_count = 100000; // CSV行数
$column_count = 50; // CSV列数
$csv_string = NULL;
for ($row = 1; $row <= $row_count; $row++) {
$row_data = $row;
for ($col = 1; $col < $column_count; $col++) {
$row_data .= CSV_DELIMITER . rand(0, 10000);
}
$csv_string .= $row_data . PHP_EOL;
}
return $csv_string;
}

/**
* CSV => Array
*
* @param resource $csv_file CSVファイルハンドル
*
* @return array 配列データ(全レコード)
*/
function useArray($csv_file)
{
$csv = [];
while (($data = fgetcsv($csv_file, 0, CSV_DELIMITER)) !== FALSE) {
$csv[] = $data;
}
return $csv;
}

/**
* CSV => Generator
*
* @param resource $csv_file CSVファイルハンドル
*
* @return array 配列データ(1レコード)
*/
function useGenerator($csv_file)
{
while (($data = fgetcsv($csv_file, 0, CSV_DELIMITER)) !== FALSE) {
yield $data;
}
}

/**
* 実行時間計測
*
* @param resource $csv_file CSVファイルハンドル
* @param callable $callable 実行対象
*
* @return array 配列データ(1レコード)
*/
function timer($csv_file, callable $callable) {
$start = microtime(TRUE);
// 100回ループ
for ($i = 0; $i < 100; $i++) {
$callable($csv_file);
}
$stop = microtime(TRUE);
return $stop - $start;
}

// 計測用CSVデータの準備
echo 'NOW LOADING ....' . PHP_EOL;
$csv = makeCsv();
$tmp = tmpfile();
fwrite($tmp, $csv);

echo 'Go Measurement!!!' . PHP_EOL;

// メモリ消費量の計測
echo '--- Memory Check ---' . PHP_EOL;

echo ' Case Array ...' . PHP_EOL;
rewind($tmp);
$before = memory_get_usage();
$result = useArray($tmp);
$after = memory_get_usage();
echo ' ' . ($after - $before) . ' bytes' . PHP_EOL;
unset($result);

echo ' Case Generator ...' . PHP_EOL;
rewind($tmp);
$before = memory_get_usage();
$result = useGenerator($tmp);
$after = memory_get_usage();
echo ' ' . ($after - $before) . ' bytes' . PHP_EOL;
unset($result);

// 実行速度の計測
echo '--- Speed Check ---' . PHP_EOL;
echo ' Case Array ...' . PHP_EOL;
rewind($tmp);
$func = function($tmp) {foreach (useArray($tmp) as $time) {}};
echo ' ' . timer($tmp, $func) . ' sec' . PHP_EOL;

echo ' Case Generator ...' . PHP_EOL;
rewind($tmp);
$func = function($tmp) {foreach (useGenerator($tmp) as $time) {}};
echo ' ' . timer($tmp, $func) . ' sec' . PHP_EOL;

fclose($tmp);

実行結果はこんな感じになりました。

NOW LOADING ....
Go Measurement!!!
--- Memory Check ---
Case Array ...
916266008 bytes
Case Generator ...
872 bytes
--- Speed Check ---
Case Array ...
3.724415063858 sec
Case Generator ...
1.3189570903778 sec