【PHP】array_key_exists vs isset どちらが早い?

【PHP】array_key_exists vs isset どちらが早い?

PHPを使っていると色々な落とし穴がある中で、比較的気付きづらいarray_key_existsは遅いと言われる所以について採り上げたいと思います。
javaやC#などの他言語から来た人は、PHP独自の仕様で引っかかりやすいので要チェックです。

はじめに

array_key_existsは配列のキーの存在を確認するためのPHP関数です。
issetは変数がセットされているかを確認するC言語の命令です。
isset($variable[“abc”])のように記述することで、array_key_existsと同じような処理が行えます。
※NULLの取り扱いが異なるので、その点は注意が必要です。

結論

原則、array_key_exists・isset、どちらを使ってもパフォーマンスは変わりません。
ただし!参照渡しされている変数を評価する場合は、array_key_existsは極めて遅くなります。
参照渡しには、アンパサンド付き(&$variable)で受け取った変数のほか、グローバル変数で定義された変数も対象になります。

理由

PHP関数は参照渡しされた変数を評価するとメモリ上に複製を生成する

PHPでは、引数を受け取った際に内部ではzend_get_parameters関数により処理が行われます。
この時、受け取った引数に対して参照がある場合、新しくzvalコンテナを作って変数をコピーします。
これは、ポインタを渡すのではなく実体のコピーなので、配列のサイズが大きければ大きいほど時間が掛かります。

コピーオンライト(Copy-On-Write)

コピーオンライト (Copy-On-Write) とは、コンピュータプログラミングにおける最適化戦略の一種である。COWと略記することもある。

コンピュータ内部で、ある程度大きなデータを複製する必要が生じたとき、愚直な設計では、直ちに新たな空き領域を探して割り当て、コピーを実行する。 ところが、もし複製したデータに対する書き換えがなければその複製は無駄だったことになる。

そこで、複製を要求されても、コピーをした振りをして、とりあえず原本をそのまま参照させるが、ただし、そのままで本当に書き換えてはまずい。原本またはコピーのどちらかを書き換えようとしたときに、それを検出し、その時点ではじめて新たな空き領域を探して割り当て、コピーを実行する。これが「書き換え時にコピーする」、すなわちコピーオンライト (Copy-On-Write) の基本的な形態である。

array_key_existsはPHP関数のため、PHP仕様のコピーオンライトに従う

つまり、array_key_exists(その他のPHP関数含め)に参照渡しされた変数を引き渡すと、内部では変数のコピーをするため、数百~数万件の配列になってくると非常に遅くなります。

対してissetはPHP関数ではなくPHPの基になっているC言語の命令が直接呼ばれるため、PHP仕様のコピーオンライトは行われません。

また、「参照」とはアンパサンド付き(&$variable)で受け取った変数だけではなく、グローバル変数も含まれるため、注意が必要です。

過去にPHP公式バグトラッカーにバグじゃないか?という報告例がある

検証したらarray_key_existsは配列件数が増えるほどissetと比べて遅くなるけど、バグじゃないの?という報告があります。
この検証コードを確認すると、配列ですが気を効かせて参照渡しで受け渡した変数で評価しています。

そして、公式からの返答には「バグじゃないよ、この資料みて。PHPのコピーオンライトの仕様だよ。」と返答があり、ステータスも「Not a bug」に変更されています。

[2012-12-10 14:43 UTC] johannes@php.net
Thank you for taking the time to write to us, but this is not
a bug. Please double-check the documentation available at
http://www.php.net/manual/ and the instructions on how to report
a bug at http://bugs.php.net/how-to-report.php

References disable copy-on-write.
http://schlueters.de/blog/archives/125-Do-not-use-PHP-references.html has some details
[2012-12-10 14:43 UTC] johannes@php.net
-Status: Open
+Status: Not a bug

ただし、これはPHP5までの話、PHP7では参照渡しの変数をPHP関数で評価してもコピーはされない

PHP7ユーザーでここまで読まれた方にはガッカリさせてしまいますが、これはPHP5までの仕様です。
PHP7では見直されたのか、参照渡しの変数を評価しても変数コピーがされませんでした。

雑感

他の言語だと配列は何もしなくても参照渡しで気にする必要もないけど、PHPはコピーオンライト仕様で受け取った先で配列を書き換えるとコピーしてしまうので、なまじ知識があるから陥りがちな問題です。
こんなややこしいというか面倒くさい言語なので私はPHP嫌いなんですが、2019年にTIOBE社の検索されている言語ランキングにPHPは8位にいるんですよね…。

既にPHPで作られたシステムが多くて、ちょっと書いたら動いてしまう敷居の低さ、最近のトレンドをがっつり抑えているフレームワーク「Laravel」の存在が人気の理由かなーと思いますが、きちんとした物を作ろうと思うと見えない裏ルール一杯で言語としてもパフォーマンスも酷いので仕事の新規案件で使う言語ではないでしょう。。

AngularがJavaScriptから静的型付けのTypeScriptに移り変わってきているように、Webアプリも高度で複雑なものが求められる中で動的型付けのPHPはリスキーな選択肢です。PHPプログラマーは玉石混合なので特に…

プログラミングカテゴリの最新記事