Composer の使い方(その3:オートローダーの最適化)
前回、「Composer の使い方(その2:スクリプト)」の続きです。今回はオートローダーの最適化についてです。
オートローダーの最適化
デフォルトでは、Composer のオートローダーはクラスの名前を解決する際に、ファイルシステムのチェックを行っています。 新しくファイルが追加されたり削除されたりしても、オートローダーがそのファイルをすぐに検出して使用できるようするためなのですが、 この処理が有効であるがために、処理速度が大幅に低下している状態になっています。 開発環境などではとても便利なのですが、本番環境では不要な処理ですし、処理速度を少しでも向上させたいと考えるでしょう。 このため、このオートローダーの処理を最適化するための方法がいくつかあります。
最適化レベル1:Class map generation
最適化レベル1は、クラスマップというファイルを生成するというものです。 このクラスマップというのは、どのクラスがどのファイルにあるかというのが一覧で記載されたファイルです。 あらかじめ全てのファイルの場所を先に調べておくことで、実行時にファイルシステムのチェックを無くし、処理を高速化しようというものです。
このクラスマップというのは PHP ファイルです。 そのため、PHP 5.6 以降では、このクラスマップも OPcache にキャッシュされるため、初期化時間が大幅に短縮されます。 OPcache が有効になっていることを確認すると、クラスマップはほぼ瞬時に読み込まれ、クラスの読み込みが高速になります。
この最適化レベル1は、本番環境では常に有効にしておいた方が良いでしょう。
使い方
最適化レベル1を有効にするには、下記のいずれかを行います。
-
composer.json に「
"optimize-autoloader": true
」という設定値を加える
(※ 開発環境ではオススメしません) -
install
コマンドもしくは、update
コマンドを実行する際に、-o
もしくは--optimize-autoloader
オプションをつけて実行するphp composer.phar install -o
php composer.phar update -o
-
dump-autoload
コマンドを実行する際に、-o
もしくは--optimize
オプションをつけて実行するphp composer.phar dump-autoload -o
問題点
唯一の問題は、クラスが見つからない場合に、そのファイルを探すためにファイルシステムをチェックし、処理が遅くなる可能性がある点です。 この問題を解決するために、さらにもう1段階の最適化ができる最適化レベル2があります。
最適化レベル2にはAとBの2種類の方法がありますが、AとBは同時に使用することはできません。どちらか片方のみ使用できます。
最適化レベル2/A:Authoritative class maps
最適化レベル2/Aは、最適化レベル1のクラスマップをさらに発展させたものです。 最適化レベル2/Aを有効にすると、自動的に最適化レベル1のクラスマップも有効になります。
最適化レベル1と2/Aの違いは、クラスマップに無いクラスを読み込もうとした場合の動作の違いです。 最適化レベル1では、クラスマップに無いファイルでも、PSR-4のオートローダーによってファイルシステムをチェックし、読み込める場合は読み込むことができますが、 最適化レベル2/Aでは、クラスマップに無いファイルは読み込むことができません。 クラスマップを正しく生成できていることが大前提となります。
クラスマップに無いファイルを読み込むことなんてあるのか?と思うかもしれませんが、意外と簡単に発生します。 例えば、独自のライブラリなどを使用している場合などです。composer.json が下記のような設定になっているとしましょう。
{ "name": "hoge/hoge", "require": { "monolog/monolog": "1.0.*", "guzzlehttp/guzzle": "7.0" }, "autoload": { "psr-4": { "Foo\\": "library/Foo/" } } }
require にあるパッケージのクラスは、Composer の install
コマンドや require
コマンドで追加/削除を行っていれば、クラスマップから漏れることはないでしょう。
ですが、autoload > psr-4 > Foo に関しては、"library/Foo/" というローカルのディレクトリにファイルがあるので、クラスマップを生成した後に、
そのディレクトリのファイルを変更したりすると簡単にクラスマップと食い違うという状況が発生します。
つまり、そのディレクトリに変更を加える度に毎回クラスマップを再生成しないといけなくなるということです。
しかしながら、こういった状況が起きるのは開発環境ぐらいで、本番環境においてはデプロイ時等に必ずクラスマップを再作成するようにすれば、 最適化レベル2/Aで問題ないということになりますので、速度を向上させたい方は最適化レベル2/Aまでやってしまったほうが良いでしょう。
使い方
最適化レベル2/Aを有効にするには、下記のいずれかを行います。
-
composer.json に「
"classmap-authoritative": true
」という設定値を加える
(※ 開発環境ではオススメしません) -
install
コマンドもしくは、update
コマンドを実行する際に、-a
もしくは--classmap-authoritative
オプションをつけて実行するphp composer.phar install -a
php composer.phar update -a
-
dump-autoload
コマンドを実行する際に、-a
もしくは--classmap-authoritative
オプションをつけて実行するphp composer.phar dump-autoload -a
最適化レベル2/B:APCu cache
デフォルトの Composer のオートローダーもしくは、最適化レベル1のオートローダーは、 クラスマップに無いファイルの場合、PSR-4のオートローダーによってファイルシステムをチェックしますが、 この最適化レベル2/Bでは、さらにそのチェックした結果を、クラスが見つかったかどうかに関係なく常に APCu キャッシュに追加します。 そうすることで、次のリクエストでは、ファイルシステムをチェックすることなく、すぐに結果を返すことができるというものです。
この最適化レベル2/Bは、最適化レベル1で生成されるクラスマップは自動的に生成されません。 もし、最適化レベル1のクラスマップが必要であれば、最適化レベル1を手動で有効にしたうえで、最適化レベル2/Bを有効にする必要があります。 つまり、最適化レベル1と最適化レベル2/Bは組み合わせて使えるということです。 最適化レベル1が無効な場合でも動作しますし、最適化レベル1と2/Bを同時に有効にした状態でも動作します。
ただ、この最適化レベル2/Bを使う機会は少ないと思われます。 APCu が必要なのですが、APCu が使用できない環境もあるでしょう。 また、最適化レベル1だけでも OPcache が使用されるため、処理速度も充分に早いと思われます。
考えられる利用用途としては、クラスがあまりにも多く、 最適化レベル1で作成されるクラスマップがあまりにも巨大なファイルになってしまうような場合であれば、 APCu キャッシュの方が有利になる場合があるのかもしれません(詳しいベンチマーク等はとっていないので断言はできませんが)。 その場合は、最適化レベル1と組み合わせたりせず、最適化レベル2/Bのみで使用することになるでしょう。
使い方
最適化レベル2/Bを有効にするには、下記のいずれかを行います。
-
composer.json に「
"apcu-autoloader": true
」という設定値を加える
(※ 開発環境ではオススメしません) -
install
コマンドもしくは、update
コマンドを実行する際に、--apcu-autoloader
オプションをつけて実行する# 最適化レベル2/Bのみで使用する場合 php composer.phar install --apcu-autoloader # 最適化レベル1と2/Bを組み合わせて使用する場合 php composer.phar install -o --apcu-autoloader
# 最適化レベル2/Bのみで使用する場合 php composer.phar update --apcu-autoloader # 最適化レベル1と2/Bを組み合わせて使用する場合 php composer.phar update -o --apcu-autoloader
-
dump-autoload
コマンドを実行する際に、--apcu
オプションをつけて実行する# 最適化レベル2/Bのみで使用する場合 php composer.phar dump-autoload --apcu # 最適化レベル1と2/Bを組み合わせて使用する場合 php composer.phar dump-autoload -o --apcu