ハウテレビジョン開発者ブログ

『外資就活ドットコム』を日夜開発している技術陣がプログラミングネタ・業務改善ネタ・よしなしごとについて記していきます。

【パフォーマンス】XDebugとqcachegrindによるPHPアプリのプロファイリング【改善】

f:id:fuyumi3:20141113194437p:plain

こんにちは! 来年統計検定の1級を受けることになった祖山(@who_you_me)です。 過去問を見たらガチすぎて震えてます。

【PDF注意】 http://www.toukei-kentei.jp/about/pastpaper/2013/2013grade1.pdf

はじめに

我々の運営する外資就活ドットコムでは、特にトップページにおいて読み込み速度が非常に遅いという問題がありました。

CDNで有名なAkamaiのレポートによると、ECサイト利用者の半数近くは2秒以内にページがロードされることを期待しているようです(少し古いデータですが)。 ところが外資就活ドットコムはこの2秒という基準を下回ってしまっていました。 我々のサイトはECサイトではないものの、ユーザーに快適にブラウジングしてもらうには遅すぎる数値です。

というわけで、早速チューニングに取り掛かりました。 パフォーマンスチューニングはとても奥が深く、職人的技量が要求される作業です。 私は職人ではないので(笑)、特にインフラ周りのチューニングについては勉強不足な部分も多いのですが、プロファイラを使ったアプリケーションのパフォーマンスチューニングでは大きな改善を達成することができました。

そのため今回は、XDebugを使ってPHPアプリをプロファイリングし、qcachegrindで可視化して調査するまでの環境整備についてご紹介します。

プロファイラとは

プログラムの性能を解析するプログラムです。

どのような順番で処理が行われたか、またどの処理がどの処理を呼び出しているかなどを、各々の処理にかかった時間とともに記録してくれるので、プログラムのどの処理がボトルネックとなっているかを特定することができます。

環境

外資就活ドットコムはnginx + php-fpm + CakePHPで動いています。

もちろん本番環境でプロファイラを動かすわけにはいかないので、開発環境で計測することになります。 そのため本番と同様の環境というわけにはいかないのですが、できるだけ本番に近づけるために、デバッグの設定などは本番と合わせておくと良いでしょう。

我々は手許のMacにVagrantでVMを立ち上げて開発環境としているので、下図のような流れになります。

f:id:fuyumi3:20141113194011p:plain

XDebugを使ってプロファイルを取得する

それでは、Vagrantで立ち上げたVMにXDebugをインストールして、プロファイルを取得してみましょう。

XDebugのインストール

peclで入ります。 sudo権限が必要な場合が多いでしょう。

sudo pecl install xdebug

私の手許のマシンでは、最初にインストールしようとしたら「makeがない」と怒られました。 環境によっては事前に他にもいくつかパッケージをインストールしておく必要があるかも知れません。

インストールに成功すると、出力内容の下の方にライブラリへのパスが表示されます。

Build process completed successfully
Installing '/usr/lib/php5/20090626/xdebug.so'

これは次の設定で使うので控えておきましょう。

プロファイラを有効化する

インストールしただけではプロファイラは有効にならないので、php.iniの末尾に次の内容を追記します。

zend_extension = /usr/lib/php5/20090626/xdebug.so    # xdebugへのパスを指定
xdebug.profiler_enable = 1    # プロファイラを有効化
xdebug.profiler_output_dir = /tmp    # どのディレクトリに出力するか
xdebug.profiler_output_name = callgrind.%R.%t    # プロファイラのファイル名

xdebug.profiler_output_nameで使用できる修飾子については下記を参照してください。
http://www.xdebug.org/docs/all_settings#trace_output_name

php.iniを更新したらphp-fpmの再起動をお忘れなく。

プロファイルしたいページにアクセスし、ファイルが出力されているか確認する。

ページに実際にアクセスします。 様々な要因により数値は変動すると予想されるので、何度かアクセスしてプロファイルを複数取っておいた方が良いでしょう。

サーバにログインして、php.iniで指定した場所にファイルが作成されているか確認します。 中身はテキストファイルで、下のようになっています。

version: 1
creator: xdebug 2.2.5
cmd: /path/to/index.php
part: 1
positions: line

events: Time

fl=php:internal
fn=php::defined
24 13

fl=php:internal
fn=php::define
25 8

fl=php:internal
fn=php::defined
37 3

fl=php:internal
fn=php::dirname
38 4

fl=php:internal
fn=php::dirname
38 1

fl=php:internal
fn=php::dirname
38 1

なんとなく中身を想像できなくはない気もしますが、このままでは何がなんだか分かりません。 という訳で可視化ツールが必要となります。

qcachegrindで可視化する

ここからの作業はMacで行います。 事前にさきほど取得したファイルをSCP等でダウンロードしておきましょう。

可視化ツールにはqcachegrindを使いました。 プロファイルの可視化にはkachegrindというツールが有名らしいですが、頭文字の通りKDEで動作するものであり、Macでは使えません。 そこでkachegrindをforkし、Qt上で動作するようにしたものがqcachegrindということです。

qcachegrindのインストール

homebrewでは入ります。 GraphvizとQtが必要です。

brew install graphviz qt qcachegrind

qcachegrindを使う

コマンドラインから起動するとウィンドウが立ち上がります。

qcachegrind

f:id:fuyumi3:20141113182706p:plain

メニューバーから File -> Open でさきほど落としてきたファイルを指定します。 するとプロファイルが表示されます!

f:id:fuyumi3:20141113194437p:plain

上の画像は例のためCakePHPのコアの処理ばかり表示されていますが、実際のページでプロファイルした場合には、下記のような流れで問題の把握から対策に繋げることができます。

  • このメソッドの処理が重い。なんでだろう?
    • モデルのfindに時間がかかってるみたい
      • 複雑なクエリを吐いてるみたいだからクエリを改善しよう
      • あるいは複雑な処理はDBじゃなくてアプリに任せよう
    • 内部APIへのリクエストが異常に多いようだ
      • 非効率なデータの取り方をしている
      • リファクタリングしよう

効率的なfindの書き方などにはコツがあったりもするのですが、本筋とは関係ないのでまたの機会にご紹介できればと思います。

まとめ

アプリが非効率な処理をしているかどうかは、このようなツールを使わなくとも直接コードを読めば分かることも多いでしょう。

しかしながら、何も手掛かりなしに闇雲に全てのコードを見ているのは非常に骨の折れる作業です。 また「リファクタリング」と口で言うのは簡単ですが、大規模なリファクタリングには大変な労力とリスクが発生します。

80:20の法則がここにも当てはまるかは分かりませんが、パフォーマンスに影響を与えるような非効率な処理は、コードの一部に偏在していることが多いでしょう。 プロファイラを使って問題のある箇所をピンポイントで発見し、その部分に集中的に対応するができれば、効率的にサイトパフォーマンスを高めることができます。

ちなみに我々の取り組みでは、こうしたコードの改善に加え、キャッシュの最適化やPHPのアクセラレータのチューニング等も行った結果、わずか数日で応答速度を 四倍 以上に高めることができました!

もちろん、こんなに上手くいくことは稀ですが、今後もUXを追求し続けて行くために、プロファイラは必須のツールとなっています。 まだ使ってことがないという人は、ぜひ使ってみてはいかがでしょうか。