俺のLaravelがこんなに遅いわけない
タグ: Laravel4
ちょっと前に(2ヶ月も前でした)Laravelを含めたPHPフレームワークのベンチマークが紹介されていました。
やけにLaravel4が遅かったのですで、おかしいと思いながらも、別段悪意のある記事ではなさそうなので、スルーしました。
でも、たいていのLaravelユーザーは、こう思っているでしょう。「俺の、私の、Laravelがこんなに遅いわけない!」
そうですね。気になっているのでは、年が越せません。年内に解決しておきましょう。良い新年を迎えるために、ちょっと検証してみましょう。
EC2インスタンス
元記事によるとEC2のマイクロインスタンス上でのテストということです。残念ながら、どのOSを使用しているのか書かれていません。そこで、やはり標準的なところで、Amazon Linux(64ビット)を選びました。元記事によりますと、Composerの実行時にメモリが足りなくなったとのことです。32ビットのほうが多分メモリは食わないのですが、速度的には64ビットが有利なはず。そしてサーバーのスタンダードが64ビットに移行しているようですので、今回は64ビットを選びました。
マイクロインスタンスでも、乗っかるマシンはたしか複数存在していたはずです。(うる覚えですが、Linux系とWindows系で違ったような気がします。仮想化の手法だったでしょうか。)ですから、元記事とはハード的な条件の違いが起きる可能性はあります。
LAMP
LAMPのインストールはAWSのチュートリアルに従いました。グループの設定を行っても、ec2-userからの書き込みができないので、ごちゃごちゃしました。(Apacheのユーザー/グループはapache/apacheです。ですからユーザーapacheをwwwグループに入れてあげれば、解決しました。)それと、Laravel4の動作には、php_mcryptモジュール(そして多分mbstringも…)が必要なため、追加でインストールしました。(Laravelの作者のTaylor氏は必要ないと行っていますが、依存パッケージのSymfonyが使っているために、無いとエラーになる場合があります。)
後は比較するためのベンチマークコードとして、元記事にあるパラメーター取得サンプルと同じコードを用意しました。(Bladeありのパターンです。実際はLaravelをインストールした後の作業になります。)
デフォルトでMod Rewriteが動作するはずですが、働きません。時間がもったいないためindex.php/testへ直接アクセスします。(ネット上に存在するAmazon LinuxでMod Rewriteが動作しないという質問は、ほとんどドキュメントルートのディレクトリーでオーバーライトがNoneのままになっているのが原因です。Amazon Linuxの問題というより、単にApacheの設定が行われていないだけです。)
Laravelのインストール
元記事では、メモリが足らないとエラーになっているようですが、Composerでインストール完了できました。4.1では依存パッケージが減っているのが影響しているのかもしれません。単に選択したOSの違いかもしれません。ちなみに4.1から導入されたインストーラーでも無事インストールできました。
ベンチマーク
元記事に紹介されているベンチマークパッケージ(ベンチマークと名乗っていますが、実効速度とメモリ使用量を表示するだけですから、プロファイラーと呼んだほうが適当でしょう)をComposerで導入し、index.phpで結果を表示するように改造しました。
<?php require __DIR__.'/../bootstrap/autoload.php'; $bench = new Ubench; $bench->start(); $app = require_once __DIR__.'/../bootstrap/start.php'; $app->run(); $bench->end(); echo $bench->getTime(); echo $bench->getMemoryPeak();
結果
速度は55ms-60msと、元記事のBladeなしのパターンとほぼ一緒です。何度か繰り返した結果ですから、Bladeテンプレートがコンパイルされ、その結果HTMLのビューと速度が変わらないのか、もしくは本当に速度が早くなっているかでしょう。元記事は記事の日付から4.0と推測されますが、現バージョンは4.1です。または、単にOSや乗っかっているハードの違いかもしれません。
さて、ここからがLaravelマジックです。私のローカル環境では、クラスファイルをまとめる、optimize Artisanコマンドを使っても、大して変化ありませんが、この環境では劇的な改善が見られました。
php artisan optimize --force
これでどの環境であっても、指定されているクラスファイルをまとめ、Composerのクラスのダンプを-oオプション付きで実行されます。その結果は、15ms-19msにまで早くなりました。EC2の環境では、各ファイルへアクセスするためのオーバーヘッドが大きいのでしょう。(実際は、マイクロインスタンスがEBSのみの使用のため、オーバーヘッドがかかっているのでしょう。単に多くのファイルを対象にgrepするだけでも遅いですから、1ファイルにアクセスするためのオーバーヘッドの問題だと思います。マイクロ以上のインスタンスなら、通常のハードディスクやSSDが利用できるため、ここまで劇的に変化があるかは疑問です。多分、最適化しなくてももっと早いでしょう。)(追記:EC2インスタンスでは「ハードディスク」は、「インスタンスストレージ」と名付けられていました。実際にはEBSとさほど変わりませんでした。)
更に、APCモジュールをインストールして、試してみましたら、9ms-10ms、時々20ms位でした。20msの時は多分、キャッシュされているコンパイル済みクラスが期限切れになり、再コンパイルを行っているのではないかと推測しています。
ここで、php artisan optimizeでまとめファイルを削除し、Composerのクラスも-oなしのダンプ状態へ戻してあげると、13ms-15ms、時々24msでした。何でも早くしてしまうAPCは、やはり恐るべきです。
結論
Laravelは遅くありません。コマンド一つで本番モードへ切り替えられます。開発中は、エラーやデバッグ時にコードが追いやすいように、クラスファイルはバラバラのままにしておき、本番環境ではそれらをまとめ、更に名前付きのクラス名と、実際のファイルの対応マップを使いクラスをオートロードするようにします。(Composerの機能)
ですから、Laravelユーザーの皆さん。胸を張ってこう言いましょう。
「俺が、私が、Laravelだ。」
追伸:メモるのを忘れましたが、メモリの使用料は最適化前が5M弱、最適化後は1.7Mだったと思います。(APC使用時です。未使用時は7.55MBで、元記事のグラフと一致します。)
ちなみにメモリの状態ですが、free実行で次のようになります。
total used free shared buffers cached Mem: 614596 419740 194856 0 29648 302440 -/+ buffers/cache: 87652 526944 Swap: 0 0 0
600MBのメモリで実質514MB空いています。多分、Composer実行してもメモリは大丈夫なはずです。やはり、OSが違うのでしょうね。
もうひとつの結論
ベンチマークは、実働環境で行わないと参考になりません。ハード構成(ディスクやメモリの速度)などにより、大きく値は異なります。ですから、速度を元にフレームワークを選定しようとしているなら、他の人のベンチマークを当てにするより、自分で試してみることをおすすめします。できれば、自分が動作させるアプリの機能を試せる、実地に即したテストプログラムを作成することをおすすめします。大抵の場合、"hello world"をいくら早く表示しても、余り意味がありません。(PHPをそのまま使用したほうが早いに決まっています。)
実際のアプリでDBなどのアクセスが入るのであれば、通常多くの実行時間はこちらに取られることになります。また、フレームワーク自身ではなく、PHPの実行自体を上げてくれるHHVMなどの技術、コードキャッシュのAPCなどにより、フレームワーク云々するより、コードの実行時間は上がります。設計としてメモリ上のキャッシュシステムを上手く使用すれば、ディスク等のアクセスによるスピード低下を大幅に防げるでしょう。
さらに、各フレームワークには最適化の技法が備わっています。メジャーフレームワークをちょっとかじっただけな人は、重くて遅いという結論をすぐに出しますが、使い込んでいる人に言わせると、きちんと最適化していない、そこまでの技術レベルに達していないということだそうです。
そして、Laravel使用者によく知られている格言は、「スピードには開発スピードも含まれる」というものです。現実的でないテストコードの速さに元に判断するよりは、そのフレームワークが自分やチーム、会社に合っているかを考慮しましょう。そのためにも、現実に即した簡単なテストプログラムを作成してみるのは、役に立つはずです。