徹底検証!PHP最適化Tips

第4回 制御構造,for文にまつわる噂を検証

この記事を読むのに必要な時間:およそ 4 分

はじめに

今回はfor文に関するtipsについて解説していきます。

for文の条件式にはcount($array)のような関数をいれない(変数に格納)

for文の条件式に関数を入れると遅くなるというtipsです。

例えば配列の要素数だけfor文を用いてループを回したいときに,配列の要素数を取得できるcount()関数を使って下記のように書くことができます。

for ($j=0; $j<count($array); $j++) {}

このtipsでは下記のようにあらかじめ変数に格納してからの使用を勧めてます。

$count = count($array);
for ($j=0; $j<$count; $j++) {}

まずはサンプルプログラムを用意し,ベンチマークをとってみます。

benchmark_for.php

<?php
$t = microtime(true);
$i = 0;
$a = null;
$array = array(1,2,3,4,5,6,7,8,9,10);
while ($i < 1000) {
   $count = count($array);
   for ($j=0;$j<$count;$j++) {
   }
   ++$i;        
}
$tmp = microtime(true) - $t;
var_dump($tmp);
?>

benchmark_for_use_count.php

<?php
$t = microtime(true);
$i = 0;
$a = null;
$array = array(1,2,3,4,5,6,7,8,9,10);
while($i < 1000) {
   for ($j=0;$j<count($array);$j++) {
   }
   ++$i;        
}
$tmp = microtime(true) - $t;
var_dump($tmp);
?>

ベンチマーク結果

$ php benchmark_for.php
float(0.00143694877625)
$ php benchmark_for_use_count.php
float(0.00325417518616)

tips通りfor文の条件式に関数を用いないほうが速い結果となりました。それではどうしてこのような結果になるのか検証していきたいと思います。

vldを用いた解析

今回ははじめにvldを用いて解析を行います。vld(Vulcan Logic Disassembler)はPHP用のディスアセンブラで,opcodeレベルでのPHPコードの解析に役立ちます。

まずは,vldをインストールします。

$ tar xzf vld-0.9.1.tgz
$ cd vld-0.9.1/
$ phpize
$ ./configure
$ make
$ sudo make install

上記の手順でインストールすることができます。

それではvldを使ってみます。サンプルコードとして簡単なfor文のコードを用意します。

1 <?php
2 for ($i=0;$i<count(1);$i++) {
3     echo '';
4 }
5 ?>

実行方法はvld.active=1をphp.iniに設定するか,コマンドラインオプションに付け加えるとvldが有効になります。

kajidai@laputa:~$ php -dvld.active=1 e.php
Branch analysis from position: 0
Jump found. Position 1 = 10, Position 2 = 8
Branch analysis from position: 10
Return found
Branch analysis from position: 8
Jump found. Position 1 = 5
Branch analysis from position: 5
Jump found. Position 1 = 1
Branch analysis from position: 1
filename:       /home/kajidai/e.php
function name:  (null)
number of ops:  12
compiled vars:  !0 = $i
line     #  op                           fetch          ext  return  operands
-------------------------------------------------------------------------------
  2     0  ASSIGN                                                   !0, 0
        1  SEND_VAL                                                 1
        2  DO_FCALL                                      1          'count'
        3  IS_SMALLER                                       ~2      !0, $1
        4  JMPZNZ                                        8          ~2, ->10
        5  POST_INC                                         ~3      !0
        6  FREE                                                     ~3
        7  JMP                                                      ->1
  3     8  ECHO                                                     ''
  4     9  JMP                                                      ->5
  6    10  RETURN                                                   1
       11* ZEND_HANDLE_EXCEPTION
               

実行結果は上記のようになりました。それでは,出力されたopcodeをもとに解析していきましょう。

2:DO_FCALLでcount()が実行されます。その結果をもとに3:IS_SMALLERで比較が行われ, 4:JMPZNZでは3:IS_SMALLERの結果がfalseであれば10:RETURNにジャンプし終了します。trueであればextended valueに設定されてる8:ECHOまで処理がジャンプし,forループ内のステートメントが実行され,9:JMPで5へジャンプしPOST_INCが実行され$iがインクリメントされます。そして7:JMPで1:SEND_VALへ処理がジャンプし再び2:DO_FCALLでcount()が実行されます。

このようにforループが続く間,条件式の評価が行われるたびにcount()が実行されることになります。

著者プロフィール

梶原大輔(かじわらだいすけ)

大学卒業後,ヤフー株式会社に入社し,Yahoo!ビデオキャストなどのサービス開発に従事。

現在はグリー株式会社でバックエンドシステムの開発を担当。

URLhttp://d.hatena.ne.jp/kajidai/

コメント

コメントの記入