PHPのarray系組込関数の一部を高速化する
とあるシステムをPHPで作っていた時のお話。
それなりに大きな配列を操作したり、小さい配列をループ内で操作したとき、
応答速度が少し遅い・・・なんて場面にぶつかってしまいました。
Xhprof なんかを使ってプロファイリングしてみると、
array_diff、array_unique、array_intersect あたりがボトルネックになっていることが分かりました。
少し調べてみると、こういった関数の高速版を公開している人がいましたので、メモがてら紹介します。
※使用は自己責任で。
array_diff
array_diff は、配列の差分を返します。(PHP: array_diff - Manual)
これの高速版がstackoverflowにありました。
以下はコードの抜粋。
O(n)になってますね。特にデメリットも思い浮かばず。
<?php function my_array_diff( $a, $b ) { $map = $out = array(); foreach( $a as $val ) $map[$val] = 1; foreach( $b as $val ) if ( isset( $map[$val] ) ) $map[$val] = 0; foreach( $map as $val => $ok ) if( $ok ) $out[] = $val; return $out; } ?>
array_unique
array_unique は、配列の重複要素を除いて返します。(PHP: array_unique - Manual)
これの高速版がpuremango.co.ukにありました。
以下はコードの抜粋。
実装の発想が面白い。array_flip(PHP: array_flip - Manual)をうまく使っています。
array_flipでキーと値を逆転。そしてまたarray_flipすることで、重複するキーが上書きで消えます。
最初に array_reverse しているのは、array_unique の挙動に近づけるためかな。
<?php function fast_unique($input) { // Code documented at: http://www.puremango.co.uk/?p=1039 return array_flip(array_flip(array_reverse($input,true))); } ?>
array_intersect
array_intersect は、配列の論理積みたいなもの(と勝手に思ってます)。(PHP: array_intersect - Manual)
これの高速版が、sitepoint.com にありました。
以下はコードの抜粋。
組込 array_intersect の限定的なもののようで、値が数値ではなく文字列とかだと
遅いようです。DBからIDのみ抽出し、論理積をとりたい時などには大活躍ですね。
<?php function my_array_intersect( $s1, $s2 ) { $i = 0; $j = 0; $N = count( $s1 ); $M = count( $s2 ); $intersection = array(); while ( $i < $N && $j < $M ) { if ( $s1[$i] < $s2[$j] ) $i++; else if ( $s1[$i] > $s2[$j] ) $j++; else { $intersection[] = $s1[$i]; $i++; $j++; } } return $intersection; } ?>