はじめに
今回は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()が実行されることになります。

