SOFTELメモ

</> 技術者募集

【php】array_key_exists は isset より1000倍以上遅いことがある

※これはphp5.6以前の問題で、php7系では発生しません。

php

通常は、array_key_exists(‘hoge’, $arr) と isset($arr[‘hoge’]) はほとんど処理速度に違いがない。

ただ、状況によっては、array_key_exists を使った処理が isset より極端に遅いことがある。

検証

// 速い(issetは言語構造)
$a = range(0, 10000);
for ($i = 0; $i < 10000; ++$i) {
        isset($a[$i]);
}
// → 0.0029 秒
// 速い(参照を作ってもあまり変わらない)
$a = range(0, 10000);
$b = &$a;
for ($i = 0; $i < 10000; ++$i) {
        isset($b[$i]);
}
// → 0.0029 秒
// 速い(関数呼び出しのオーバーヘッドかな?程度の誤差)
$a = range(0, 10000);
for ($i = 0; $i < 10000; ++$i) {
        array_key_exists($i, $a);
}
// → 0.0033 秒
// 遅い(参照を作ると遅い)
$a = range(0, 10000);
$b = &$a;
for ($i = 0; $i < 10000; ++$i) {
        array_key_exists($i, $b);
}
// → 9.5 秒
// 遅い(なんと参照を作られても遅い)
$a = range(0, 10000);
$b = &$a;
for ($i = 0; $i < 10000; ++$i) {
        array_key_exists($i, $a);
}
// → 9.5 秒

なぜ?

phpのソースを確認すると、array_key_exists は、引数を受け取るときに zend_parse_parameters() を使っています。

「zend_get_parameters() は、参照をチェックします。 参照が見つかった場合には、独立した zval コンテナを新しく作成し、参照先のデータをそこにコピーし、 その新しく作成した (他とは分離している) コンテナへのポインタを返します。」なのだそうで、参照を作るとコピーを作るようになってしまうので遅くなるということのようです。

補足

array_key_exists と isset は動作が若干違います。

$arr[‘hoge’] = NULL のとき、array_key_exists(‘hoge’, $arr) は true、isset($arr[‘hoge’]) は false を返します。

高速化のためなどで置き換えるときは注意しましょう。

関連するメモ

コメント