倍違う!? if文の書き方4パターン……と見せかけて本当は10パターン

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
立小便禁止ってのがシュールで良いね

この記事を三行にまとめると

一瞬ってのは0.36秒だそうです
elseは使わない方が早い可能性が微レ存
確定的に明らか……と言えないこともない
PHPでif文を使う時、書き方は大きく分けて4つのパターンがあると思います。

一つは一般的な書き方。

if($i % 2 == 0) {
  echo $i.'は偶数です';
} else {
  echo $i.'は奇数です';
}

これをelse文を使わずにif文を二つに分けて書くこともできます。

if($i % 2 == 0) {
  echo $i.'は偶数です';
}

if($i % 2 == 1) {
  echo $i.'は奇数です';
}

得られる結果は同じです。片方のif文がtrueであればもう片方のif文は必ずfalseになりますから。

あとはこの二パターンを1行ずつPHPタグで囲う書き方が考えられます。

//elseあり
<?php if($i % 2 == 0) : ?>
  <?php echo $i.'は偶数です' ?>
<?php else : ?>
  <?php echo $i.'は奇数です' ?>
<?php endif ?>

//elseなし
<?php if($i % 2 == 0) : ?>
  <?php echo $i.'は偶数です' ?>
<?php endif ?>

<?php if($i % 2 == 1) : ?>
  <?php echo $i.'は奇数です' ?>
<?php endif ?>

HTMLと絡めて書くときはこっちの書き方をすることもあるかと思います。

どのパターンも結果的には同じ処理になりますが、はたしてこの四つの書き方は速度に差はあるのか? 特に一行ずつPHPタグで囲った場合とそうでない場合は速度の差が顕著に現れるのか。ふと疑問に思いました。

そんな疑問を解決するため、書き方が違うと速度に差が出るのかどうかを実際に検証してみることにしました。

今回は適当に100万回くらいのループの中で簡単なif文を書いて処理を実行し、それを10回くらい繰り返して平均時間を出してみました。

PHPのバージョンは7.1です。



第一のパターン

else文を使った一般的なパターンです。

$results = array();
$start = microtime(true);
for($i = 0; $i < 1000000; $i++) {
  if($i % 2 == 0) {
    $results[] = $i / 2;
  } else {
     $results[] = $i * 2;
  }
}
$end = microtime(true);
echo $end - $start;

偶数だったら2で割って奇数だったら2をかける。それを100万回繰り返すだけです。

10回の実行結果はこうなりました。

0.40
0.30
0.26
0.30
0.24
0.25
0.23
0.33
0.23
0.22

平均タイム・・・0.28秒



早いんだか遅いんだかよく分かりませんね。ちなみにほんの短い時間のことを「一瞬」って言ったりしますが、仏教的な時間の数え方でいくと一瞬ってのは0.36秒だそうです。つまりこの結果は一瞬にも満たない出来事だったってことですね。

とりあえずこれが基準タイムになります。



第二のパターン

次はelse文を使わずif文を二つに分けた書き方です。

$results = array();
$start = microtime(true);
for($i = 0; $i < 1000000; $i++) {
  if($i % 2 == 0) {
    $results[] = $i / 2;
  }
  if($i % 2 == 1) {
    $results[] = $i * 2;
  }
}
$end = microtime(true);
echo $end - $start;

そして10回の実行結果がこちら。

0.24
0.27
0.27
0.32
0.25
0.22
0.24
0.24
0.24
0.22

平均タイム・・・0.25秒



ほぼ差はないですがさっきよりわずかに早いですね。僕の予想ではほぼ同じになるか、差がついたとしてもelseを使うより遅くなると思ってたんですが……。

もしかしてif文ってなるべくelseを使わない方が早いんですかね?



第三のパターン

次はelse文を使って一行ずつPHPタグで囲う場合。

<?php $results = array() ?>
<?php $start = microtime(true) ?>
<?php for($i = 0; $i < 1000000; $i++) : ?>
  <?php if($i % 2 == 0) : ?>
    <?php $results[] = $i / 2 ?>
  <?php else : ?>
    <?php $results[] = $i * 2 ?>
  <?php endif ?>
<?php endfor ?>
<?php $end = microtime(true) ?>
<?php echo round($end - $start, 2) ?>

if文だけじゃなくて他のところも全部PHPタグで囲ってるんで厳密にif文だけの差っていう計測にはなりませんが、まあこれでもしも上記の二パターンと大きく差が出るのであれば、少なくとも一行ずつPHPタグで囲う場合とそうじゃない場合では明らかな速度の違いがあるぞってことは分かりますね。

何となく直感的な印象としてはPHPタグで一行ずつ囲うと処理が遅くなりそうな気がするのですが、実際の結果は……これだっ!

0.38
0.35
0.36
0.48
0.35
0.32
0.34
0.35
0.34
0.33

平均タイム・・・0.36秒



第一のパターンが0.28秒でしたから、その差は0.08秒。僕たちの人間的な感覚からすると0.08秒なんてたいした差じゃないですが、1.3倍違うって考えるとコンピュータ的には結構な差なのかもしれないですね。

少なくとも第一のパターンはだいたいが0.2秒台だったのに対し、こっちは10回の結果の中で一度も0.3秒を下回っていないのは確かです。これを有意差があると思うかどうかは……あなた次第です(丸投げ)

何はともあれ最後のパターンも見てみましょう。



第四のパターン

PHPで一行ずつ囲って、なおかつelseを使わないパターン。

<php $results = array() >
<php $start = microtime(true) >
<php for($i = 0; $i < 1000000; $i++) : >
  <php if($i % 2 == 0) : >
    <php $results[] = $i / 2 >
  <php endif >
  <php if($i % 2 == 1) : >
    <php $results[] = $i * 2 >
  <php endif >
<php endfor >
<php $end = microtime(true) >
<php echo round($end - $start, 2) >

先ほどelseを使わない方が微妙に早いという結果を踏まえると第三のパターンよりは若干早いという結果が予想されますが……はたして?

0.37
0.40
0.41
0.42
0.40
0.38
0.39
0.39
0.38
0.38

平均タイム・・・0.39秒



おお……何と全パターンの中で一番遅いという結果になりました。第三のパターンよりも遅い。第二のパターンと比べても1.6倍遅いです。第二のパターンでは0.3秒台後半は一つもないのに対し、こっちのパターンでは逆に0.3秒台前半が一度もない。これは明確に差があると思って良いのかもしれませんね。

ただ、elseを使わない方が早いという仮説は微妙な感じでしょうか。



メモリの使用量

どうやら一行ずつPHPタグで囲わない方が実行速度が早くなりそうだというのは見えてきました。じゃあメモリの使用量はどうだろうか。何となく速度に比例しそうだから、これもPHPタグで囲わない方がメモリの消費は抑えられそうですが、何事も百聞は一見にしかずと言いますからね。実際にやって確かめてみましょう。

メモリの使用量はmemory_get_peak_usageで取得した値で比較します。

結果はこうなりました。

第一のパターン・・・35.7MB
第二のパターン・・・35.7MB
第三のパターン・・・35.7MB
第四のパターン・・・35.7MB

特に変わらずですね。まあ実行結果として得られる配列の中身とかは一緒だから、こっちに関してはそういう差はないか。



おまけのパターン

ふと思ったんだけど、一行ずつPHPタグで囲う場合と囲わない場合の差があるのは良いとして、じゃあif文やfor文の閉じ方をかっこにするか「endif」「endfor」にするかで差はあるんでしょうか。それもちょっとやってみました。

$results = array();
$start = microtime(true);
for($i = 0; $i < 1000000; $i++) :
  if($i % 2 == 0) :
    $results[] = $i / 2;
  else :
    $results[] = $i * 2;
  endif;
endfor;
$end = microtime(true);
echo round($end - $start, 2);

第一のパターンを書き直すとこうですね。逆に第三のパターンを書き直すとこうなる。

<?php $results = array() ?>
<?php $start = microtime(true) ?>
<?php for($i = 0; $i < 1000000; $i++) { ?>
  <?php if($i % 2 == 0) { ?>
    <?php $results[] = $i / 2 ?>
  <?php } else { ?>
    <?php $results[] = $i * 2 ?>
  <?php } ?>
<?php } ?>
<?php $end = microtime(true) ?>
<?php echo round($end - $start, 2) ?>

こんな感じで四パターンのかっことendを入れ替えて同じように10回の平均を取ってみました。

結果はこうだ! デデン!



第一のパターン・・・0.24秒
第二のパターン・・・0.25秒
第三のパターン・・・0.35秒
第四のパターン・・・0.4秒



基本的にはかっこをendに変えても大きな違いはないみたいですね。第一パターンだけちょびっと早くなったけど……でも他がほとんど同じって考えるとこの結果は誤差の範囲なのかな。

一行ずつPHPタグで囲った方が速度が遅い点は変わりないですね。やはりこの有意差は無視できないレベルな気がします。



さらなるおまけ

この記事を書いてるうちにもういっこ思いついちゃったんで、ついでに検証します。

今回みたいな分岐が二パターンしかない時はelseの後ろを省略して書くことができますが、これを明示的にelseif文で書いた場合は差が出るのか。つまり第一と第三のパターンを以下のように書き換えたらどうなるか。

//第一のパターン
$results = array();
$start = microtime(true);
for($i = 0; $i < 1000000; $i++) {
  if($i % 2 == 0) {
    $results[] = $i / 2;
  } else if($i % 2 == 1) {
    $results[] = $i * 2;
  }
}
$end = microtime(true);
echo round($end - $start, 2).'';

//第三のパターン
<?php $results = array() ?>
<?php $start = microtime(true) ?>
<?php for($i = 0; $i < 1000000; $i++) : ?>
  <?php if($i % 2 == 0) : ?>
    <?php $results[] = $i / 2 ?>
  <?php elseif($i % 2 == 1) : ?>
    <?php $results[] = $i * 2 ?>
  <?php endif ?>
<?php endfor ?>
<?php $end = microtime(true) ?>
<?php echo round($end - $start, 2) ?>

第一のパターン・・・0.3秒
第三のパターン・・・0.44秒

こうなりました。ちょっとだけ遅くなった感じがあるかな……?








今回は以上のような結果になりました。この結果から予想されることは、「elseは使わない方が早い可能性が微レ存だけど、PHPタグで囲う方がコスト高めなのは確定的に明らか……と言えないこともない」ってところでしょうか。倍違うというタイトル通りの結果ではなかったですけど。

ただしものすごく差があるというわけではないので、コードの可読性とどっちを優先すべきかって言われたら、可読性優先でも全く問題はないと思います。

あと今回は分岐が二種類の場合のみでテストしましたけど、elseが十個くらいに分岐するような処理だとまた違った結果が得られるかもしれません。でも僕の場合、分岐が三つ以上になるとswitch文を多用したがるからな……。
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください