PHPのfor文の高速化の話題で、教科書等によく記載されている
for($i = 0; $i < 10 $i++){
//処理を書く
}
ではなく、
for($i = 0; $i < 10 ++$i){
//処理を書く
}
にした方が良いということを時々見かける。
実際のところはどうなのか?を処理速度の測定とVLDで確認してみる。
ちなみに$i++と++$iの違いは、if文等で、式を実行してから$iに1を加える(インクリメント)か、$iに1を加えてから式を実行するか?の違いがある。
$i = 2;
if($i++ > 2){
//通過しない
}
では、2行目のif文の式の箇所がfalse(偽)になるが、
$i = 2;
if(++$i > 2){
//通過する
}
であればtrue(真)になる。
環境
Ubuntu 20.04
PHP 7.4.6
まずは処理速度の方を確認する。
これからの話はすべてファイル名をfor.phpで話を進める。
前者の$i++の方のコードは下記のようにする。
<?php
$start = microtime(true);
for($i = 0; $i < 100000000; $i++){
//処理を書く
}
$end = microtime(true);
echo $end - $start;
実行と結果は下記の通り
$ php /path/to/dir/for.php 0.51271295547485
続いて、++$iの方を確認してみる。
<?php
$start = microtime(true);
for($i = 0; $i < 100000000; ++$i){
//処理を書く
}
$end = microtime(true);
echo $end - $start;
$ php /path/to/dir/for.php 0.29786896705627
後者のコードにするだけで、処理速度が3/5程になった。
オペコードはどうなるのか?を確認してみる。
<?php
for($i = 0; $i < 10; $i++){
//処理を書く
}
実行と結果は下記の通り
$ php -d vld.active=1 -d vld.execute=0 /path/to/dir/for.php
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 42) Position 1 = 4
Branch analysis from position: 4
2 jumps found. (Code = 44) Position 1 = 6, Position 2 = 2
Branch analysis from position: 6
1 jumps found. (Code = 62) Position 1 = -2
Branch analysis from position: 2
2 jumps found. (Code = 44) Position 1 = 6, Position 2 = 2
Branch analysis from position: 6
Branch analysis from position: 2
filename: /path/to/dir/for.php
function name: (null)
number of ops: 7
compiled vars: !0 = $i
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > ASSIGN !0, 0
1 > JMP ->4
2 > POST_INC ~2 !0
3 FREE ~2
4 > IS_SMALLER ~3 !0, 10
5 > JMPNZ ~3, ->2
5 6 > > RETURN 1
branch: # 0; line: 2- 2; sop: 0; eop: 1; out0: 4
branch: # 2; line: 2- 2; sop: 2; eop: 3; out0: 4
branch: # 4; line: 2- 2; sop: 4; eop: 5; out0: 6; out1: 2; out2: 6; out3: 2
branch: # 6; line: 5- 5; sop: 6; eop: 6; out0: -2
path #1: 0, 4, 6,
path #2: 0, 4, 2, 4, 6,
path #3: 0, 4, 2, 4, 6,
path #4: 0, 4, 6,
path #5: 0, 4, 2, 4, 6,
path #6: 0, 4, 2, 4, 6,
オペコードに関する細かい話は置いといて、続いて下記のコードの方を確認する。
<?php
for($i = 0; $i < 10; ++$i){
//処理を書く
}
実行と結果は下記の通り
$ php -d vld.active=1 -d vld.execute=0 /path/to/dir/for.php
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 42) Position 1 = 3
Branch analysis from position: 3
2 jumps found. (Code = 44) Position 1 = 5, Position 2 = 2
Branch analysis from position: 5
1 jumps found. (Code = 62) Position 1 = -2
Branch analysis from position: 2
2 jumps found. (Code = 44) Position 1 = 5, Position 2 = 2
Branch analysis from position: 5
Branch analysis from position: 2
filename: /path/to/dir/for.php
function name: (null)
number of ops: 6
compiled vars: !0 = $i
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > ASSIGN !0, 0
1 > JMP ->3
2 > PRE_INC !0
3 > IS_SMALLER ~3 !0, 10
4 > JMPNZ ~3, ->2
5 5 > > RETURN 1
branch: # 0; line: 2- 2; sop: 0; eop: 1; out0: 3
branch: # 2; line: 2- 2; sop: 2; eop: 2; out0: 3
branch: # 3; line: 2- 2; sop: 3; eop: 4; out0: 5; out1: 2; out2: 5; out3: 2
branch: # 5; line: 5- 5; sop: 5; eop: 5; out0: -2
path #1: 0, 3, 5,
path #2: 0, 3, 2, 3, 5,
path #3: 0, 3, 2, 3, 5,
path #4: 0, 3, 5,
path #5: 0, 3, 2, 3, 5,
path #6: 0, 3, 2, 3, 5,
各々の結果で太字の箇所が違いだけれども、$i++から++$iにすると、オペコードが一行減っている。
おそらく、メモリの使用量でも少し削減されているはずだ。
for内の三番目の処理では、$i++と++$iのどちらも意味は変わらないので、積極的に++$iの方を使っていった方が良さそうだ。
余談
$i++の方にあるFREEのコードは内部で確保した値の解放という意味があるらしい。IS_SMALLERは2つの値の比較の処理(ここでは0行目で定義した変数の値とfor内の二番目の値の10の比較)なので、処理前に値を確保するためのメモリを確保しているのだろうか?
PHPの処理において、メモリの確保の処理は遅くなる傾向があるらしいので、++$iの方が速いのは、メモリの確保と解放が無いことに因るものでありそうだ。





