[PHP]require(require_once)するときは必ず絶対パスを使いましょう
require_once '/path/to/a.php';
当たり前ですが'/path/to/a.php'というファイルがダイレクトにオープンされて読み込まれます。何の問題ないですね。
一方、絶対じゃないパス名(ファイル名だけとか)を書いたらどうなるでしょうか?
require_once 'PEAR.php'
この場合、PHP実行エンジンはinclude_pathで指定されたディレクトリを順番に探索します。もしinclude_pathが'/home/dqn/app:/home/dqn/extlib:/home/dqn/lib:/usr/share/pear'となっている場合、 PHP実行エンジンは下記のようにファイルを探索します。
/home/dqn/app/PEAR.php あるかな・・・ないぉ!
/home/dqn/extlib/PEAR.php あるかな・・・ないぉ!
/home/dqn/lib/PEAR.php あるかな・・・ないぉ!
/usr/share/pear/PEAR.php あるかな・・・あった!
しかもこの探索処理はHTTPリクエストが来るたびに行われてしまうので、require_onceがたくさん書かれているウェブアプリケーションでは無視できないコストになります。(オートローダ使えという話もありますが、今回はあくまでrequireの仕組みについての解説です。)
実際に挙動を確かめてみる
straceコマンドを使えばこの挙動を実際に見ることができます。まず実験用のPHPスクリプトを用意します。
hello.php
<?php
ini_set('include_path', '/home/dqn/app:/home/dqn/extlib:/home/dqn/lib:/usr/share/pear');
require_once 'PEAR.php';
echo "hello\n";
次にApache設定ファイルを編集して、子プロセスの数(MaxClients)を1に制限します。
/etc/httpd/conf/httpd.conf
# prefork MPM
<IfModule prefork.c>
[...]
MaxClients 1 ← コレ
[...]
</IfModule>
次に、straceでhttpd子プロセスを監視します。
sudo strace -p 12345
(12345のところは実際には子プロセスのプロセスIDです。)ここでブラウザからhello.phpを叩くと、だーっとシステムコールのログが流れます。
途中よく見てみるとこのようなログが出ています。
lstat("/home/dqn/app/PEAR.php", 0x7fffd92a4a60) = -1 ENOENT (No such file or directory)
lstat("/home/dqn/extlib/PEAR.php", 0x7fffd92a4a60) = -1 ENOENT (No such file or directory)
lstat("/home/dqn/lib/PEAR.php", 0x7fffd92a4a60) = -1 ENOENT (No such file or directory)
lstat("/usr/share/pear/PEAR.php", {st_mode=S_IFREG|0644, st_size=33913, ...}) = 0
PHP実行エンジンが、カーネルに対してlstatシステムコールを発行して、空振りに終わった(No such file or directory)様子が見てとれます。無駄なことをしているのがよくわかりますね。
自分のプロジェクトでやってみたら・・・
実際に、自分が昔作った大きめのPHPアプリでrequire_onceをしまくっている(150箇所も!)やつがあったので、絶対パスに書き換えて測定してみました。ab -c 1 -n 200 http://localhost/myapp/index.php
改修前 (require_once 'lib/hoge.php' みたいな書き方してた )
Requests per second: 6.99 [#/sec] (mean)
Time per request: 142.977 [ms] (mean)
改修後 (require_onceを絶対パスで書き直した)
Requests per second: 22.70 [#/sec] (mean)
Time per request: 44.060 [ms] (mean)
すんげーーーーーーー!!!!!!!
3倍も速くなった!!!
まとめ
require/includeは絶対パスで書こうstraceはプログラマのともだち
(そしてオートローダ使えるなら使おう。)
補足
「絶対パスで書くと環境依存になる」という声がありましたが、絶対パスを相対的に指定すれば問題ありません。require_once dirname(__FILE__) . '/lib/hoge.php';
参考
- Gree Fast Processor: PHPを3倍(くらい)速く | GREE Engineers' Blog
- PHP: include - Manual
- クラスの読み込み - Zend Framework Performance Guide - Zend Framework
- PHP5 外部ファイルの読み込みの最適化 require require_once incule incule_once
カテゴリ:
PHP