なんとなく結果が分かってはいるけれど、基礎的なPHPの構文がどれだけ処理に 時間がかかっているかを算出してみました。 マシンスペックによって結果は異なってきますが、簡単なスクリプトを用意して 速度を測ってみます。
まずは文字列の解析にどれだけ時間がかかっているかをチェックしてみました。 ダブルクォーテーション内は変数が展開される為、その分のオーバーヘッドがか かると思います。
<?php
ini_set('max_execution_time','100');
$loop_counter = 1000000;
/**
* クォーテーションチェック
*/
// PHP5からはmicrotime()に引数を渡すとfloatで返してくれるのです☆
$single_start_time = microtime(true);
$single_check = array();
for ($i=1; $i<=$loop_counter; $i++) {
$single_check["abc"] = "PHPプロ!";
}
$single_end_time = microtime(true);
$double_check = array();
$double_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; $i++) {
$double_check['abc'] = 'PHPプロ!';
}
$double_end_time = microtime(true);
$single_loop_time = $single_end_time - $single_start_time;
$double_loop_time = $double_end_time - $double_start_time;
print $single_loop_time."<br>\n";
print $double_loop_time."<br>\n";
?>
多少の差はあるものの、大きなオーバーヘッドは発生しないようです。 次にインクリメントの記述方法で速度が変わるかをチェックしてみました。 パターンは
- $i++
- ++$i
- $i = $i + 1
- $i += 1
の4パターンです。
<?php
/**
* インクリメントチェック
*/
$check1_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; $i++){}
$check1_end_time = microtime(true);
$check2_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; ++$i){}
$check2_end_time = microtime(true);
$check3_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; $i = $i + 1){}
$check3_end_time = microtime(true);
$check4_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; $i += 1){}
$check4_end_time = microtime(true);
$check1_loop_time = $check1_end_time - $check1_start_time;
$check2_loop_time = $check2_end_time - $check2_start_time;
$check3_loop_time = $check3_end_time - $check3_start_time;
$check4_loop_time = $check4_end_time - $check4_start_time;
print $check1_loop_time."<br>\n";
print $check2_loop_time."<br>\n";
print $check3_loop_time."<br>\n";
print $check4_loop_time."<br>\n";
?>
どうやら前置加算子が一番早いようです。 前置加算子・後置加算子の違いはこちら(http://www.phppro.jp/phpmanual/php/language.operators.increment.html)
最後にループの比較をしてみました。 大きく分けて
- for文
- while文
- do-while文
- foreach文
の速度を比較しています。
一つのfor文をとってもいくつかの記述方法があります。
- ループ範囲をブレス{}で囲う
- 1行ループの{}ブレスを省く
- ループ範囲を for(): endfor; で囲う
の比較をしてみました。 また、foreach文とwhile(each())を使ったケースを比較しました。
<?php
/**
* for文チェック
*/
$for1_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; ++$i) {$a=0;}
$for1_end_time = microtime(true);
/**
* for文チェック(記述別)
*/
$for2_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; ++$i)
$a=0;
$for2_end_time = microtime(true);
/**
* for文チェック(記述別)
*/
$for3_start_time = microtime(true);
for ($i=1; $i<=$loop_counter; ++$i):
$a=0;
endfor;
$for3_end_time = microtime(true);
/**
* while文チェック
*/
$while_counter = 1;
$while_start_time = microtime(true);
while ($while_counter <= $loop_counter) {
$a=0;
++$while_counter;
}
$while_end_time = microtime(true);
/**
* do-while文チェック
*/
$dowhile_counter = 1;
$dowhile_start_time = microtime(true);
do {
$a=0;
++$dowhile_counter;
} while ($dowhile_counter < $loop_counter);
$dowhile_end_time = microtime(true);
// $loop_counter個の配列を作っておきます
$test_array = array();
for ($i=1; $i<=$loop_counter; $i++) {
$test_array[$i] = $i;
}
/**
* while(each())文チェック
*/
$list_while_start_time = microtime(true);
while (list($key, $value) = each($test_array)) {$a=0;}
$list_while_end_time = microtime(true);
/**
* foreach文チェック
*/
$foreach1_start_time = microtime(true);
foreach ($test_array as $key => $value) {$a=0;}
$foreach1_end_time = microtime(true);
/**
* foreach文キーなしチェック
*/
$foreach2_start_time = microtime(true);
foreach ($test_array as $value) {$a=0;}
$foreach2_end_time = microtime(true);
$for1_loop_time = $for1_end_time - $for1_start_time;
$for2_loop_time = $for2_end_time - $for2_start_time;
$for3_loop_time = $for3_end_time - $for3_start_time;
$while_loop_time = $while_end_time - $while_start_time;
$dowhile_loop_time = $dowhile_end_time - $dowhile_start_time;
$list_while_loop_time = $list_while_end_time - $list_while_start_time;
$foreach1_loop_time = $foreach1_end_time - $foreach1_start_time;
$foreach2_loop_time = $foreach2_end_time - $foreach2_start_time;
// 通常for文を1とした時の速度率を出力しています。
$for1_rate = $for1_loop_time / $for1_loop_time;
$for2_rate = $for2_loop_time / $for1_loop_time;
$for3_rate = $for3_loop_time / $for1_loop_time;
$while_rate = $while_loop_time / $for1_loop_time;
$dowhile_rate = $dowhile_loop_time / $for1_loop_time;
$list_while_rate = $list_while_loop_time / $for1_loop_time;
$foreach1_rate = $foreach1_loop_time / $for1_loop_time;
$foreach2_rate = $foreach2_loop_time / $for1_loop_time;
print $for1_rate."<br>\n";
print $for2_rate."<br>\n";
print $for3_rate."<br>\n";
print $while_rate."<br>\n";
print $dowhile_rate."<br>\n";
print $list_while_rate."<br>\n";
print $foreach1_rate."<br>\n";
print $foreach2_rate."<br>\n";
?>
for文は記述方法で処理速度に大きな違いはほとんど見られないようです。違いがあったら面白かっただけに、残念です。
次にwhileとdo-whileの比較です。若干do-whileの方が早いようですがこれ位の 差であれば、可読性を重視するなら見慣れた通常のwhile文を使うのがよさそう ですね。
最後にforeachとwhile(each())の比較です。私は普段からforeachを使っており、遅いとの噂があったのでwhileに期待をしていたのですが、大差をつけてのforeach勝ちでした。 また、foreachの要素キーを取得する$keyを省くとかなり1.1倍~1.3倍程高速という結果になりました。ループ内で要素キーを使わない場合は省いてしまったほうが良さそうです。
実行するマシンスペックや、ループコード内に不平等はありますが、処理速度を総合的に見るとwhile文が一番早いようです。
上記結果を踏まえて、コード規約を一度見直しては如何でしょうか?
この記事は、アシアルが運営するPHP開発者のためのポータル&コミュニティサイト「PHPプロ!」で毎週配信しているPHP・TIPSメーリングリストを再録したものです。
同サイトでは、他にもPHP最新ニュースや、困ったときのQ&A掲示板、初心者向けのPHP講座など、PHP開発者をサポートする情報を掲載しています。