なんとなく結果が分かってはいるけれど、基礎的な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開発者をサポートする情報を掲載しています。