PHPにはタイプヒントと呼ばれる引数/戻り値データ型指定機能があります。これは便利な機能ですが性能に影響を与えます。
データ型チェックのオーバーヘッド
性能への影響は簡単なベンチマークで確認できます。単純な加算を行う関数fを定義して確認しました。PHPのバージョンは7.0.19です。
タイプヒントを指定しない場合
$ php -r 'function f($a, $b) { return $a + $b; } $s=microtime(true); for($i=0;$i<100000000;$i++) { f(2,3); } echo microtime(true) - $s, "\n";' 2.4526360034943
intタイプヒントを指定した場合
$ php -r 'function f(int $a, int $b) { return $a + $b; } $s=microtime(true); for($i=0;$i<100000000;$i++) { f(2,3); } echo microtime(true) - $s, "\n";' 3.1499469280243
3割ほど性能が低下しました。
assertを使用し型チェックした場合(assert有効)
assert文を利用してもタイプヒントと同じチェックが可能です。
$ php -r 'function f($a, $b) { assert(is_int($a)); assert(is_int($b)); return $a + $b; } $s=microtime(true); for($i=0;$i<100000000;$i++) { f(2,3); } echo microtime(true) - $s, "\n";' 12.682933807373
assert文は関数のように実行される上、is_int関数でデータ型チェックをしているので大きなオーバーヘッドがあります。タイプヒントを利用しない場合と比べ5倍近くの時間が必要です。
assertを無効化した場合(assertをコンパイルしない)
PHP 7.0からzend_assertion=-1を指定することにより、assert文自体をコンパイルさえしなくすることが可能になりました。これによりassertを記述のオーバーヘッドはほぼゼロになります。
実行結果はタイプヒントを利用しない場合とほぼ同じ結果になります。
$ php -d zend.assertions=-1 -r 'function f($a, $b) { assert(is_int($a)); assert(is_int($b)); return $a + $b; } $s=microtime(true); for($i=0;$i<100000000;$i++) { f(2,3); } echo microtime(true) - $s, "\n";' 2.4744720458984
参考:zend_assertion=0の場合はコンパイルはしますが、実行時にスキップします。このためオーバーヘッドが残ります。
$ php -d zend.assertions=0 -r 'function f($a, $b) { assert(is_int($a)); assert(is_int($b)); return $a + $b; } $s=microtime(true); for($i=0;$i<100000000;$i++) { f(2,3); } echo microtime(true) - $s, "\n";' 2.9517409801483
契約プログラミングの場合、assertを用いてデータ型(+範囲、長さ、形式なども)をチェックします。assert文のコンパイルはphp.ini設定で無効化でき、assert文を用いた契約条件チェックのオーバーヘッドはゼロになります。
まとめ
動的なスクリプト系言語の場合、データ型チェックは”コンパイル時”には行なえません。この為、タイプヒントを指定すると実行時にデータ型チェックが行われます。
契約プログラミングを採用している場合(現状ではassertを利用している場合)、データ型チェックは事前条件の1つとしタイプヒントは利用しない方がより良い性能になります。
データ型のチェックは「セキュリティ対策やソフトウェアの実行が正しく行われる事」を保証する為に役立ちますが、セキュリティ対策に於て”最も重要な文字列型”の中身を一切チェックできません。数値演算など、正しい実行結果を保証したい場合に数値型である事をだけをチェックしても十分ではありません。
データ型制約に過大な期待を持っているケースも散見されますが、データ型制約では十分なセキュリティや実行の正確性は仕組み的に保証できません。無いよりは良い、という程度です。データ型制約に過大な期待を持っている場合、安全でないコード、正しく実行できないコードを書く原因にもなります。この場合、無いほうがマシ、と言える場合もあります。
PHPなど、動的な言語の場合、データ型制約は少なくないオーバーヘッドを必要します。オーバーヘッドがある割には”セキュリティ的”、”コードの正しさ的”にも役立たない点は覚えて置いた方が良いです。
とは言ってもデータ型制約は”最低限の安全性と実行正確性”を保証するには便利な機能です。オーバーヘッドが気にならない場合は積極的に使っても良い機能であることも覚えておいてください。