ホームページ >バックエンド開発 >PHPチュートリアル >PHPプロファイラー
私たち開発者が本当に苦手なことがあるとすれば、それは推測することです。私たちはアプリケーションのどの部分が遅いのかを知っているつもりで、それらの最適化に多くの時間を費やしていますが、実際にはボトルネックは別の場所にあることがよくあります。唯一賢明な方法は、いくつかのプロファイリング ツールを使用して測定することです。
PHP で使用できるプロファイラーがいくつかありますが、最も一般的に使用されるのは Xdebug で、KCacheGrind/WinCacheGrind/MacCallGrind と組み合わせると、関数呼び出しのグラフを表示したり、各関数に費やされた時間。
この記事では、Facebook で開発され、2009 年 3 月に (Apache 2.0 ライセンスの下で) オープンソース化された別のプロファイラー、XHProf を試してみます。 XHProf は関数レベルの階層プロファイラーであり、生データを収集するための PHP 拡張機能 (C で記述) と、レポート/UI レイヤー用のいくつかの PHP スクリプトを備えています。
Wikipedia によると:
プロファイリング、フォーム動的プログラム分析 (静的コード分析とは対照的) は、プログラムの実行中に収集された情報を使用してプログラムの動作を調査することです。この分析の通常の目的は、プログラムのどのセクションを最適化するかを決定することです。全体的な速度を上げるか、メモリ要件を減らすか、場合によってはその両方を行うため
つまり、プロファイラーは、プログラム イベントが発生したときに、そのイベントがシステムに与える影響を記録し、さまざまな手法でデータを収集するツールです。プロファイラーの中には、メモリと CPU 使用率のみを測定するものもありますが、完全な関数呼び出しトレース、時間、集計データなど、より多くの情報を収集するプロファイラーもあります。それらはフラットまたは階層的にすることができます。つまり、各関数を単独で、またはその子孫の完全なツリーを使用してそのコンテキスト内で分析できます。
インストール現時点では、XHProf は Linux と FreeBSD でのみ利用可能です (そして、これらで動作することが期待されています) Mac OS)。
それを入手する最も簡単な方法は、PEAR インストーラー (パッケージ ホーム) を経由することです:
apt-get install php5-common pecl config-set preferred_state betapecl install xhprof
config.m4 が見つからないためにエラーが発生した場合でも、手動で拡張機能をビルドできます。次の手順を使用します。
wget http://pecl.php.net/get/xhprof-0.9.2.tgztar xvf xhprof-0.9.2.tgzcd ./xhprof-0.9.2/extension/phpize./configure --with-php-config=/usr/local/bin/php-configmakemake installmake test
XHProf をインストールしたら、それを有効にする必要があります。 php.ini を開いて追加します
[xhprof]extension=xhprof.soxhprof.output_dir="/var/tmp/xhprof"
ここで、/var/tmp/xhprof は実行ごとにプロファイル データを収集するディレクトリです。
Apache を再起動すると、XHProf 拡張機能が有効になるはずです (「」で確認してください)。 php -m」を実行すると、これが当てはまります)。
コード ブロックのプロファイルを作成するコード ブロックをプロファイルするには、次の 2 つの呼び出しをラップします:
// your code // start profilingxhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY); // the code you want to profile // stop profiler$xhprof_data = xhprof_disable();
$xhprof_data 配列をダンプして表示することができます各関数呼び出しの生のプロファイラー データ (呼び出し数、経過時間、CPU 時間、メモリ使用量、ピーク メモリ使用量) を任意の時点で検査したい場合に使用します。
xhprof_enable() は、プロファイリング対象を制御するためのいくつかのフラグを受け入れます。デフォルトでは、呼び出し回数と経過時間のみがプロファイリングされますが、メモリと CPU 使用率を追加できます。開発環境で有効になっていることを確認してください (ただし、実稼働環境で使用する場合は、オーバーヘッドが高くなるため、CPU タイマーを無効にしてください)。出力のノイズが多すぎる場合は、XHPROF_FLAGS_NO_BUILTINS フラグを使用して組み込み PHP 関数のレポートを無効にするか、次のように 2 番目のパラメーターを渡すことで特定の関数を除外することもできます。コードの小さなブロックではなく、ページの完全な概要が得られると便利です。そのような概要は、配列ダンプではなく、表またはグラフとしてフォーマットする方がよいでしょう。この目的のために、XHProf は、使用するために有効にする必要がある便利な UI を提供します。
XHProf UI のコードは、xhprof_html/ ディレクトリと xhprof_lib/ ディレクトリにあります。これらが /usr/local/lib/php/ に作成されていると仮定すると、そのディレクトリを /var/www/xhprof/ にシンボリックリンクして、DocumentRoot から利用できるようにすることができます。
また、2 つの PHP ファイルを作成する必要もあります:
/usr /share/php5/utilities/xhprof/header.php
// ignore builtin functions and call_user_func* during profiling$ignore = array('call_user_func', 'call_user_func_array');xhprof_enable(0, array('ignored_functions' => $ignore));
/usr/share/php5/utilities/xhprof/footer.php
<?phpif (extension_loaded('xhprof')) { include_once '/usr/local/lib/php/xhprof_lib/utils/xhprof_lib.php'; include_once '/usr/local/lib/php/xhprof_lib/utils/xhprof_runs.php'; xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY);}
最後に、それらを .htaccess ファイルに追加します。したがって、これらは自動的にページの先頭/末尾に追加されます。これは、ページをロードするたびに、ワンクリックで新しいプロファイラー データを取得でき、解析および分析するための外部ツールを必要としないため、時間を大幅に節約できます。
How to Use XHProf UIIf you click on the link at the bottom of the page, a new page opens with the profiler data:
As you can see, the page has a nice summary with overall statistics, and a table with all the function calls, that can be sorted by many parameters:
Memory usage and CPU time are further differentiated into “Inclusive” and “Exclusive”: Inclusive Time includes the time spent in the function itself and in all the descendant functions; Exclusive Time only measures time spent in the function itself, without including the descendant calls.
Finally, in the report page there’s also an input box to filter by function name, and a link to the full call graph, similar to the one you would get with *CacheGrind. Make sure you have GraphViz installed (apt-get install graphviz).
As stated in the documentation, XHProf keeps track of only one level of calling context and is therefore only able to answer questions about a function looking either one level up or down. This is rarely a problem, since you can drill down or up at any level. Clicking on a function name in fact will show details about the current function, the parent (caller) and the children (called).
As a rule of thumb, when we’re ready to optimise our application, we should start sorting the data by CPU (exclusive), and look at the top of the list. The functions at the top are the most expensive ones, so that’s where we should focus our efforts and start optimising/refactoring. If there’s nothing obviously wrong, we drill-down and see if there’s something more evident at an inner level. After every change, we run the profiler again, to see the progress (or lack thereof). Once we are happy, we sort by Memory Usage or Wall time, and start again.
Here’s a quick summary if you want to print a step-by-step worksheet as a reference:
Profiling can be an extremely tedious process, because it requires a lot of patience, and a lot of time staring at numbers in a table (how exciting, eh?). Hopefully, the results of this process are exciting: improvements are often dramatic, since rewriting the slowest parts of the code (and not those we think are slow) has a considerable effect on the overall page load and ultimately on the user’s experience. The advantage of using good tools is that they help maintaining discipline and focus, and thus in building experience.
Diffs and Aggregate ReportsXHProf has a nice feature to get the differences between two different runs, clearly marked in red and green colours. This way it is easy to instantly see the improvements after every change.
To view the report use a URL of the form:
http://%xhprof-ui-address%/index.php?run1=XXX&run2=YYY&source=myapp
Where XXX and YYY are run ids, and the namespace is “myapp” .
Also, it’s possible to aggregate the results of different runs, to “normalise” the reports. To do so, separate the run IDs with a comma in the URL:
http://%xhprof-ui-address%/index.php?run=XXX,YYY,ZZZ&source=myappスケーラビリティへの道 ベースラインを測定する
あらゆる旅と同様、自分がどこにいるのか、どこに行きたいのかを把握する必要があります。アプリケーションをスケーリングする必要がある場合は、ターゲット (ユーザー/秒、メモリ使用量、ページ生成時間) と制約 (アプリケーション、フレームワーク、サーバー リソース) を把握する必要があります。
コーディングを開始する前に、アプリケーションを使用する場合、フレームワークを使用している場合は、そのベースラインを測定することをお勧めします。たとえば、これは空の Zend Framework プロジェクトの概要です (注意: 同じ考慮事項がどのフレームワークにも当てはまります。ZF を悪いフレームワークとして取り上げるつもりはありません):
これは、フレームワーク自体を最適化するか、またはフレームワーク自体を最適化しない限り、それを示しています。ページ全体をキャッシュする場合、2.5MB 未満のメモリを使用したり、ページ読み込みあたりの関数呼び出しを 1500 未満にすることはできません。これが出発点です。
フレームワーク自体のプロファイリングは、単なるスタイルの練習ではなく、フレームワークがどのように機能するか、さまざまなコンポーネントがいかに (非) 効率的であるかについて目を見張るものです。
一般的なプログラミングの実践には、これらの選択が与える影響を見て驚くような例がたくさんあります。以下にいくつかの例を示します。
「error.logging.level」という config.ini 設定があり、Zend_Config を使用してその値を読み取る場合は、$config->error->logging->level を使用する必要があります。すべての「矢印」演算子は 2 つの関数呼び出しを意味します。つまり、構成設定の値を読み取るためだけに 6 回の関数呼び出しが行われることになります。その値を頻繁に読み取る場合、またはループで読み取る場合は、その値を変数に保存することを検討してください。
ビュー ヘルパーを使用するたびに、舞台裏で多くの処理が行われます。これが呼び出しスタックです (実際にはもっと悪いですが、理解できると思います):
テンプレートをレンダリングするために Partial() を呼び出すと、現在のビュー オブジェクトが複製され、プライベートでない変数はすべて設定解除されます。これは、高価なリフレクションと非常に多くの substr() 呼び出しによって行われます。可能であれば、代わりに render() を使用してください (テンプレートが非常に小さく、何度も呼び出される場合は、ビュー ヘルパーを使用してください)。
テンプレートをレンダリングするか、モデル クラスを使用するたびに、ZF はインクルード パスをスキャンして、ロードする正しいファイルを見つけます。たとえ以前にすでにそのファイルをリクエストしたとしても。単一ページの実行で統計呼び出しが何千回行われるかを知ると驚くでしょう。幸いなことに、XHProf (この場合は strace/dtrace) を使用すると、ファイルがディスクから読み取られるたびに簡単に確認できるため、include_path の順序を最適化し、場合によっては APC を使用して、同じファイルの include_path を 2 回スキャンすることを回避できます。 .
json_encode() の代わりに Zend_Json::encode() を使用するときは、特別な理由がない限り、自分自身を棒で打つ必要があります (おそらく文字通りではありません)。呼び出しをプロファイリングして何が起こるかを確認するのは、読者の演習として残しておきます。
先ほども言ったように、私は Zend Framework を非難するつもりはありません。他のフレームワークも劣っていたり優れていたりするわけではないと確信しています。ただし重要なのは、フレームワークの各コンポーネントのコストを認識し、アプリケーションでどの構成要素を使用するかを意識的に決定できるようにすることです。
アプリケーションは外部リソースにアクセスする可能性があります。データベース、Web サービス、またはディスク上のデータ。これらは通常、最小限に抑えるべき最もコストのかかる操作です。 XHProf レポートを見たときにリストの先頭にそれらが表示されない場合は、おそらく何か問題があることを意味します。この場合、フレームワークが主なボトルネックであるか、アーキテクチャをリファクタリングする必要がある可能性があります。
場合によっては、すべてのリソースを消費する単一の呼び出しはありませんが、コードの特定の部分に関連する関数呼び出しのクラスターを見つけるのは簡単です。
言うまでもなく、これはそのコンポーネントをリファクタリングする必要があることを示す明確な指標です。
遅いコードを見つけたら、それを最適化する前に、なぜ何かをしているのか、それを行うのに適切な場所なのかを再考し、可能であれば処理する必要があるデータの量を減らします。これらの手順を経て初めて、最善の方法について悩み始めることができます。
I’m sure we all agree on the above statement, but sometimes it’s not that obvious what to look for. Or we think we already optimised everything, the reports don’t show any single resource hog, and we reached a dead end. This is when I find it useful to sort the XHProf reports by number of function calls. Usually, it is not a good indicator of the performance of a piece of code, because a single function responsible to retrieve data from an external source is a few orders of magnitude slower than many calls to an internal PHP function, for instance. On the other hand, even if PHP is fast, do we really need to call strtolower() 15000 times? Looking for odd things like this might give some hints on how we process data, and maybe come up with a better way. Too often we tend to bash a language for its slowness, and we tend to forget that usually performance issues have more to do with the implemented algorithms than with the operations used.
Here are some other code smells that might suggest we are doing something in a sub-optimal way:
Immutable functions called within loops Same content being generated twice Content that don’t change being generated every time Content being generated even if not usedAll these cases are perfect candidates for caching. Of course I’m not suggesting caching everything. Remember that memory is another limited resource, so don’t abuse it if you need to scale; the key is to spread the load uniformly across all the available resources. You have to think about the cache-hit ratio, and start caching things you hit all the times. Also, it makes little sense to cache if it takes more effort writing to the cache than you save. But more often than not, you can cache a LOT of content.
In order of effectiveness, you can use static variables, APC, memcached. But do not forget about other kind of caches that are even more effective: proxy cache (or reverse-proxy), and of course the user’s browser. If you send the correct headers, many requests will be resolved before even reaching the server!
Some of the above mentioned code smells, even if apparently obvious, are in practice not very simple to spot. For loops and content being generated more than once, it should be quite easy, just look at the number of times a certain function is called and draw your conclusions. Identifying data being processed but not used might be harder: you see the traces, and ideally you should think why you are seeing those calls at all, or why you see them in that particular place. That’s why a lot of discipline is required: you keep looking at those reports for so long that you wish you could eliminate (violently) as many calls as possible so you don’t have to look at them anymore.
Decouple ServicesDo not rely on having all the resources available on the same machine. The more you decouple the various services, the easier it is to scale horizontally. Problem is, how to identify the parts to decouple? Well, first of all think about all the services that can be logically separate from the application itself, like all the data sources, content providers, data stores, but also the data-processing routines that are effectively black boxes. Then you might look at the profiler, and see if there’s a resource-intensive routine: can you move it to another machine? Can you maybe add -say- a thin RESTful interface around it? If so, then that service can be moved out of your app, and taken care of separately (e.g. with horizontal replication, if it’s a data store, or put on a cluster behind a load balancer if it’s a data processor).
Profile Under LoadAs a last suggestion, it’s a good idea to collect profiler data under load, which is probably more representative of the real usage. To collect a random sample of profiler data, you can run a load testing tool (e.g. apache ab, siege, avalanche) and save a XHProf run every 10000 runs, by modifying the included scripts like this:
/usr/share/php5/utilities/xhprof/header.php
$xhprof_on = false;if (mt_rand(1, 10000) === 1) { $xhprof_on = true; if (extension_loaded('xhprof')) { include_once '/usr/local/lib/php/xhprof_lib/utils/xhprof_lib.php'; include_once '/usr/local/lib/php/xhprof_lib/utils/xhprof_runs.php'; xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY); }}
/usr/share/php5/utilities/xhprof/footer.php
if ($xhprof_on && extension_loaded('xhprof')) { $profiler_namespace = 'myapp'; // namespace for your application $xhprof_data = xhprof_disable(); $xhprof_runs = new XHProfRuns_Default(); $run_id = $xhprof_runs->save_run($xhprof_data, $profiler_namespace); // url to the XHProf UI libraries (change the host name and path) $profiler_url = sprintf('http://myhost.com/xhprof/xhprof_html/index.php?run=%s&source=%s', $run_id, $profiler_namespace); echo '<a href="'.$profiler_url.'" target="_blank">Profiler output</a>';}
If your load testing tool can generate reports on CPU and memory usage over time, and collect statistics on what external services are accessed and with what frequency, then by all means observe those graphs, they give a lot of information on the real behaviour of your application and its critical areas. This is a goldmine when it comes to understanding what remains to be optimised. Also make sure the response time remains as flat as possible, without too many spikes or an exponential growth as the load increases: this is a good indicator of stable code and a stable architecture.
いくつかの別れの考え本当に大幅な速度の向上とスケーラビリティの向上を達成したい場合は、多くの場合、冷酷になり、すべてに疑問を持ち、すべての愚かな質問をし、5 なぜの原則に従い、そう、チームの他の全員を困らせる準備をしなければなりません。私はそのようなことを複数回行ったと思います。心よりお詫び申し上げます。しかし、それは正当な理由によるものでした。
リソース言及されたトピックの一部についての詳細が記載されたリンクと、さらに詳しい資料:
http://mirror.facebook .net/facebook/xhprof/doc.html http://pecl.php.net/package/xhprof http://xdebug.org/docs/profiler http://derickrethans.nl/xdebug_and_tracing_memory_usage.php http://kcachegrind .sf.net/ http://sourceforge.net/projects/wincachegrind http://www.maccallgrind.com/ http://www.slideshare.net/postwait/scalable-internet-architecture
转自:
PHP プロファイラー