記事一覧

PHPコードのボトルネックを調査して改善した話



目次



ボトルネックと向き合う

ハローみなさん、エジソンです。

みなさんは、PHPで楽しくプログラムしていますか?

僕は先日、とある機能で、パフォーマンスが著しく悪く、実用に耐えられない箇所を発見しまして、その箇所の修正に取り組んでいました。その時に得た知見を、本記事で披露したいと考えました。

ある全体の作業工程の中で、特定の工程が遅い事により、全体に影響を及ぼしてしまう事を、ボトル(首)が細い事で水の通りが悪くなる事になぞらえて、ボトルネックと呼びます。

ボトルネックの特定

ボトルネックを解消する手順は以下の通りです。

  1. ボトルネックとなっている箇所を特定する
  2. 該当箇所を修正する
  3. 修正後にどれだけ処理速度が改善したかを確認する
     期待されている場合は、OK。そうでなければ、「1」に戻って再調査。

一番やってはいけない事は、「2」から取りかかる事です。はやる気持ちをぐっとこらえて、まずはボトルネックとなっている箇所の特定を行わなければなりません。なぜなら、最初から修正に取りかかると、該当箇所修正後に、処理速度がどれぐらい改善されているかを比較できなくなるからです。

さて、今回語るべき事は、「1」から「3」全てではなく、「1」のボトルネックの特定方法についてです。

では、以下のサンプルコードをご覧ください。

処理がAからCまであり、これの何処かにボトルネックがあるとします。

<?php
	// 処理A
	somethingA();
	
	// 処理B
	somethingB();
	
	// 処理C
	somethingC();
	
	function somethingA() {
		// ...
	}
	function somethingB() {
		// ...
	}
	function somethingC() {
		// ...
	}

…さて、処理の何処が原因で遅くなっているのかを調べるには、どうしたら良いでしょうか。



答えは以下の通りです。

<?php

	// 処理A
	$time_start = microtime(true);
	somethingA();
	echo "process time : " . sprintf('%.3f', microtime(true) - $time_start) . PHP_EOL;
	
	// 処理B
	$time_start = microtime(true);
	somethingB();
	echo "process time : " . sprintf('%.3f', microtime(true) - $time_start) . PHP_EOL;
	
	// 処理C
	$time_start = microtime(true);
	somethingC();
	echo "process time : " . sprintf('%.3f', microtime(true) - $time_start) . PHP_EOL;

	function somethingA() {
		// ...
	}
	function somethingB() {
		// ...
	}
	function somethingC() {
		// ...
	}


something関数の呼び出しの前後に着目してください。$time_startで現在時刻を取得して、echo部分で、再度現在時刻を取得して、それらを引く事で処理時間を出力しています。

要するに、処理時間を計測するコードを、処理の前後に地道にはさめていくだけ。ただ、それだけの事です。

PHPには便利なプロファイリングツールなどあり、こちらを使うと、自動で関数の呼び出しを記録して、後からグラフィカルなツールで表示することで、処理時間を表示する事ができます。ただし、プロファイラツールの正しい使い方を知らないと、解析は難しいですし、プロファイラを用いると、本来の処理にさらに負荷が掛かり、全体の処理速度が遅くなってしまうため、処理全体の速度を100%とした場合の、各処理の割合から、ボトルネックを見つけるという手順を追わなければなりません。

そのため、初心者は、このように処理と前後に計測コードをはさめていくのが、一番お手軽なボトルネックの判定方法なのではないかと思うわけです。

とはいえ、最終的には、プロファイラツールをビシッと使いこなせられるレベルを目指すべきとは思いますが、そんな余裕が無い場合は、この地道な方法が功を奏することがあるかと思います。

当たり前といえば、当たり前な話なんですが、今回ボトルネックの改善作業を行っていて、改めて実感したことを語りたくなったわけなのでした。

まとめ

処理の前後に計測コードを挟めるのが一番手軽でわかりやすい、ボトルネックの特定方法です。

処理時間の計測コードはイディオムです。以下を丸暗記しましょう!

	// ミリ秒出力 %.3f
$time_start = microtime(true);
something();
echo "process time : " . sprintf('%.3f', microtime(true) - $time_start) . PHP_EOL;
// ミリ秒出力 %.6f
$time_start = microtime(true);
something();
echo "process time : " . sprintf('%.6f', microtime(true) - $time_start) . PHP_EOL;

sprintfしないで出力すると、"3.0994415283203E-5"みたいな表記になるので注意が必要です。

参考
PHP - microtime
PHP - sprintf
関連記事

このエントリーをはてなブックマークに追加

コメント

コメントの投稿

非公開コメント

プロフィール

EZOLABブログへようこそ。
EZOLABは、札幌のソフトウェア会社です。

http://ezolab.co.jp