PHPのclearstatcacheのベンチマークを取ってみた。むしろ遅かった。

こんにちは。今回はPHPネタです。

PHPにはclearstatcacheという関数があります。

PHPはパフォーマンス向上のために それらの関数の戻り値をキャッシュします。しかし、ケースによっては、 キャッシュされた情報を消去したい場合もあるでしょう。 例えば、一つのスクリプト上で同じファイルが何度もチェックされ、 そのファイルが変更されたり削除されたりする可能性がある場合、 ステータスキャッシュを消去しなければならないと感じるでしょう。 このようなケースでは、clearstatcache()を使用することで ファイルの情報に関してPHPが持っているキャッシュをクリアできます。
PHP: clearstatcache – Manual

この機能はドキュメント見る限り割と古くから導入されてるそうですが、PHP 5.1でバグってました。
それは後述するとして、 このバグを引き換えに得られる性能 はどんなもんなのさ? を計測してみました。

はじめに

clearstatcacheによるバグは確認した限りではPHP 5.1にて発生しました。
PHP 5.5, 5.6においては修正されて予期したとおりの挙動になっていました。
5.2, 5.3, 5.4では確認をしていないため、その間のいつバグが治ったのかは未確認です。

clearstatcache周りで起きるバグとは

<?php

$file = 'なんか存在するファイル';

file_exists($file); // true, 当然の結果
unlink($file);

file_exists($file); // true, !?

古いPHPでこのコードを動かすとコメントのとおりになります。
ユニットテストで意図的にファイルを消すようなテストを書いていたら思い切りハマりました。
諸々調べてみるとclearstatcacheという関数に行き着きました。

まずはclearstatcacheのドキュメントを読んでみます。
※まず前提として、 明言されてない仕様 が隠されています。

PHP は存在しないファイルについての情報はキャッシュしないことにも 注意してください。もし存在しないファイルに対して file_exists() をコールする場合、ファイルを作成するまで この関数は FALSE を返します。もしファイルを作成した場合、 たとえファイルを削除したとしても TRUE を返します。 しかし、unlink() はキャッシュを自動的にクリアします。
PHP: clearstatcache - Manual

"たとえファイルを削除したとしても TRUE を返します"って変だと思わないんですかね・・・
ファイルを削除した場合キャッシュを開放とか内部実装してくれよ・・・意味わからん・・・
細かいバージョンまではわかりませんが、古いPHPにおいてはこの機能のせいでバグっています。

しかし、PHP5.5以上で確認してみると、rmdirでもキャッシュが消えてました。
環境依存なのかバージョン依存なのか、詳しく追えていません。

ベンチマークを取ってみる

期待通りの挙動を犠牲にしてまで手に入れたパフォーマンスとはどんなもんなのか。いざ実測。
ベンチマークに使用したコードはこんな感じです。

call_with_cache, call_nocacheが比較処理です。
違いは毎回clearstatcacheしてから処理を呼ぶか、clearstatcacheしないか。

先述の通りファイルが存在しない場合はキャッシュされないそうなので、テストケースから除外。
確実に存在するファイルパスである自分自身を参照しています。

テスト対象はPHPのドキュメントを信じました。

影響を受ける関数を以下に示します。 stat(), lstat(), file_exists(), is_writable(), is_readable(), is_executable(), is_file(), is_dir(), is_link(), filectime(), fileatime(), filemtime(), fileinode(), filegroup(), fileowner(), filesize(), filetype(), および fileperms().
PHP: clearstatcache - Manual

この機能に影響するiniの設定は以下のとおりです。

$ php -i | grep realpath_cache_
realpath_cache_size => 16K => 16K
realpath_cache_ttl => 120 => 120

で、肝心のベンチマーク結果は以下のとおりです。

benchmark

全般的にclearstatcacheしたほうが早い というわけのわからない結果になりました。
だったらこの機能いらないじゃん!なんだそれ!!

試しにiniの設定を書き換えリアルパスキャッシュを無効化したり、逆にrealpath_cache_sizeを16Kから16Mに増やして実行してみたところ、誤差の範囲内の違いしか出ませんでした。
なんだそれ!!!

まとめ

私の環境でベンチマーク取った限りでは、完全に無駄というかむしろ邪魔な機能という結果になりました。
どんな歴史的経緯があるのかまでは調べてませんが、なぜこんな機能作ったんだろう。

古いPHPではバグってるし、新しいPHPでも性能に寄与してないし。
久々に予想を覆す、ベンチマーク取ってよかったと感じる計測でした。