PHP - ちょっとベンチマーク
PHPってインタプリタだけど中間コード生成するよなあ〜その際にオプチマイズってしてくれるのかなあ?
実行結果(レンタルサーバでは負荷をかけられないので自宅の Solaris x86で実行)
【ループ】 - count を毎回コールする loop1 を 5000 回実行した際にかかった時間: 0.09086 ms - ループの前にカウントしておく loop2 を 5000 回実行した際にかかった時間: 0.04983 ms - foreach を使う loop3 を 5000 回実行した際にかかった時間: 0.03510 ms 【配列】 - array_push を使う array1 を 1000 回実行した際にかかった時間: 0.14758 ms - [] を使う array2 を 1000 回実行した際にかかった時間: 0.10397 ms 【インクリメント】 - $i++ を使う inc1 を 2000 回実行した際にかかった時間: 0.03648 ms - ++$i を使う inc2 を 2000 回実行した際にかかった時間: 0.03386 ms
最適化を考慮したコーディング
PHPってよくいえば律儀にインタプリトするのね・・・ちょっと高速化を考慮したコーディングしないと駄目ですね。
【PHP特有の最適化】
print より echo を利用したほうが速い
echo の文字列表示はドットによる連結('文'.'字')よりカンマ区切り('文','字')の方が速い
static にできるメソッドは static として宣言した方が速い
大きな配列変数は、必要がなくなったら unset()してメモリを解放する
ループの最大値は、ループの前にセットしておく
ループ処理には foreach を利用する
for($i=0; $i <= count($array); $i++)というように for文に count を記述すると毎回 count が動くので、forの前にカウントして変数に入れておく
マジックメソッド(__get, __set, __autoload,...)は利用しない
正規表現の利用は極力避ける(strncasecmp, strpbrk, striposといった文字列操作系の関数を利用する)
htmlspecialchars_decodeは大量のデータの変換は遅い
PHP4で行っていたころのように function htmlspecialchars_decode4($string, $quote_style=ENT_COMPAT) {
return strtr($string, array_flip(get_html_translation_table(HTML_SPECIALCHARS, $quote_style)));
}
とstrtrとarray_flipを利用した方が速いstr_ < preg_ < ereg_ < mb_ereg とかかる時間の大きさの図式になる(リプレースを考えると strtr < str_replace < preg_replaceとなる、そもそも ereg_系は利用しない)
@によるエラー制御は利用しない
インクリメント: ローカル変数 < グローバル変数 < オブジェクト変数($this->prop++)とかかる時間の大きさの図式
インクリメント: $i++ より ++$i の方が速い
if (strlen($foo) < 5){} なら if (!isset($foo{5})) {} というように isset を利用したほうが速い
処理に時間のかかる複雑な関数は拡張モジュール化にする(PECL)
もともと用意されている関数を利用する(用意されている関数は C で実装されているため)
一時的なファイルを作るときには tmpfile や tempnam を利用する
パスなどの設定パラメータは配列を serialize して キャッシュしておく
define()を大量に記述した設定ファイルをロードするより parse_ini_file() を利用する
アクセスの多いページには PHP の 出力バッファリング を利用する
【ちょっと迷う最適化】
require_onceの利用は処理を遅くするが、コードの保守性や安全のためには利用すべき?
include, require で指定するファイルはフルパスで記述するのがよいが、コードの保守性のためには?
ダブルクォート より シングルクォート の方が少し速い
すべてを OOPする必要はない(メモリを多く消費してしまうかも)
【環境の最適化など】
キャッシュの利用(memcachedや APC、XCache、eAccelerator)
通信量を減らすために Apache の mod_gzip/mod_deflate を利用する
Apache の allowoverride を "none" にする
xdebugなどを利用してコードのプロファイリングを行う
ソースコード
<?php
require_once("Benchmark/Iterate.php");
define(MAX_RUN, 5000);
$data = array();
for ($i=0; $i<100; $i++) {
$data[$i] = $i;
}
header("Content-Type: text/html;charset=euc-jp");
echo "<html>\n";
echo "<head>\n";
echo "<title>Benchmark</title>\n";
echo "</head>\n";
echo "<body>\n";
echo '【ループ】<br>';
echo '- count を毎回コールする<br>';
doBenchmark('loop1', $data, 5000);
echo '- ループの前にカウントしておく<br>';
doBenchmark('loop2', $data, 5000);
echo '- foreach を使う<br>';
doBenchmark('loop3', $data, 5000);
echo '<br>';
echo '【配列】<br>';
echo '- array_push を使う<br>';
doBenchmark('array1', $data, 1000);
echo '- [] を使う<br>';
doBenchmark('array2', $data, 1000);
echo '<br>';
echo '【インクリメント】<br>';
echo '- $i++ を使う<br>';
doBenchmark('inc1', $data, 2000);
echo '- ++$i を使う<br>';
doBenchmark('inc2', $data, 2000);
echo "</body>\n";
echo "</html>\n";
exit;
/**
* ベンチマーク関数
*
* @param function $sFuncName 関数名
* @param array $aryData 関数に渡す配列
* @param integer $iCount 実行回数
*/
function doBenchmark($sFuncName=null, $aryData=null, $iCount=MAX_RUN)
{
reset($aryData);
$benchmark = new Benchmark_Iterate;
$benchmark->run($iCount, $sFuncName, $aryData);
$result = $benchmark->get();
printf("%s を %d 回実行した際にかかった時間: %.5f ms<br>",
$sFuncName,
$result['iterations'],
$result['mean'] * 1000);
}
// count を毎回コールする
function loop1($aryData = null)
{
for ($i =0; $i < count($aryData); $i++) {
$sum += $aryData[$i];
}
}
// ループの前にカウントしておく
function loop2($aryData = null)
{
$max = count($aryData);
for ($i =0; $i < $max ; $i++) {
$sum += $aryData[$i];
}
}
// foreach を使う
function loop3($aryData = null)
{
foreach ($aryData as $data) {
$sum += $data;
}
}
// array_push を使う
function array1($aryData = null)
{
$aryBuff = array();
foreach ($aryData as $data) {
array_push($aryBuff, '<!-- '.$data.' -->');
}
}
// [] を使う
function array2($aryData = null)
{
$aryBuff = array();
foreach ($aryData as $data) {
$aryBuff[] = '<!-- '.$data.' -->';
}
}
// $i++ を使う
function inc1($aryData = null)
{
$aryBuff = array();
foreach ($aryData as $data) {
$i++;
}
}
// ++$i を使う
function inc2($aryData = null)
{
$aryBuff = array();
foreach ($aryData as $data) {
++$i;
}
}
?>