2010年3月24日水曜日

10000レコードあたりから遅く重くなる理由:array_shiftが遅いためだった(ベンチマーク比較)

Posql で 10000 レコードあたりから処理速度がかなり遅くなります。

プロファイラで調べてみたところ、
巨大な配列を array_shift() でループしてるところでかなり時間がかかっていました。

今まで気づかなかったのがアホなのですが、、
ためしに
$row = array_shift($rows);
// (処理)
$new_row[] = $row;
としていたのを
$row = $rows[$i++];
// (処理) 
$new_row[] = $row;
に変えてみたら、ビックリするくらい速くなりました。

でも これではメモリがどんどん食われていきます。
そこで、
以下のようにシフトする代わりに null で埋めていく処理にしてみました。
$row = $rows[$i];
$rows[$i] = null;
$i++;
// (処理) 
$new_row[] = $row;
結果として、array_shift() とメモリコストはほぼ同じで高速化できました。


そんなに違うの?と疑問かと思うのでベンチマークとってみました。

<?php
// 配列を作成
$array = array();

// ベンチマークタイマー読み込み
require_once 'Benchmark/Timer.php';
$bench = new Benchmark_Timer;

$bench->start();
// -------------- array_shift --------------
// 10000 のアイテムを持つ連想配列を作成
for ($i = 0; $i < 10000; $i++) {
  $array[] = array('key' => 'value' . $i);
}
// array_shift() で空になるまでシフト
while (--$i >= 0) {
  $shift = array_shift($array);
}
$bench->setMarker('array_shift');

// --------------- null fill ---------------
// 同じく 10000 の連想配列を作成
for ($i = 0; $i < 10000; $i++) {
  $array[] = array('key' => 'value' . $i);
}
// array_shift() の代わりに null で埋める
$j = 0;
while (--$i >= 0) {
  $shift = $array[$j];
  $array[$j] = null;
  $j++;
}
// 結果的に同じになるよう初期化
$array = array();

$bench->setMarker('null fill');
// -----------------------------------------
$bench->stop();
$bench->display();
?>


結果:
time indexex time%
Start1269384227.24003700-0.00%
array_shift1269384227.955818000.71578197.70%
null fill1269384227.972599000.0167812.29%
Stop1269384227.972643000.0000440.01%
total-0.732606100.00%

10000 ループだけでもここまで違いがわかります。
圧倒的に null で埋めるほうが速い
もっと早く気付けばよかった。。

0 コメント:

コメントを投稿