$arr = range(1, 100000);
のような配列があった場合の繰り返しで、
$cnt = count($arr);
for($i = 0; $i < $cnt; ++$i){
//処理を書く
}
foreach($arr as $i => $v){
//
}
上記のようなfor文かforeach文のどちらを使えば良いか迷う。
そんな時はVLDでオペコードを確認してみたり、実行速度を確認してみると迷いがなくなる。
先にPHPのVLDでfor文を見る2の記事で見たfor文の方のオペコードを確認する。
<?php
$arr = range(1, 100000);
$cnt = count($arr);
for($i = 0; $i < $cnt; ++$i){
$v = $arr[$i];
//以後の処理は省略
}
※foreachの方で配列のインデックスと値を参照できるようにforの方でも合わせた。
上記のコードの結果は下記の通り。
$ 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 = 12
Branch analysis from position: 12
2 jumps found. (Code = 44) Position 1 = 14, Position 2 = 9
Branch analysis from position: 14
1 jumps found. (Code = 62) Position 1 = -2
Branch analysis from position: 9
2 jumps found. (Code = 44) Position 1 = 14, Position 2 = 9
Branch analysis from position: 14
Branch analysis from position: 9
filename: /path/to/dir/for.php
function name: (null)
number of ops: 15
compiled vars: !0 = $arr, !1 = $cnt, !2 = $i, !3 = $v
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > INIT_FCALL 'range'
1 SEND_VAL 1
2 SEND_VAL 100000
3 DO_ICALL $4
4 ASSIGN !0, $4
3 5 COUNT ~6 !0
6 ASSIGN !1, ~6
4 7 ASSIGN !2, 0
8 > JMP ->12
5 9 > FETCH_DIM_R ~9 !0, !2
10 ASSIGN !3, ~9
4 11 PRE_INC !2
12 > IS_SMALLER ~12 !2, !1
13 > JMPNZ ~12, ->9
8 14 > > RETURN 1
branch: # 0; line: 2- 4; sop: 0; eop: 8; out0: 12
branch: # 9; line: 5- 4; sop: 9; eop: 11; out0: 12
branch: # 12; line: 4- 4; sop: 12; eop: 13; out0: 14; out1: 9; out2: 14; out3: 9
branch: # 14; line: 8- 8; sop: 14; eop: 14; out0: -2
path #1: 0, 12, 14,
path #2: 0, 12, 9, 12, 14,
path #3: 0, 12, 9, 12, 14,
path #4: 0, 12, 14,
path #5: 0, 12, 9, 12, 14,
path #6: 0, 12, 9, 12, 14,
結果に関しては特に触れずに続いてforeach文の方を見てみる。
<?php
$arr = range(1, 100000);
foreach($arr as $i => $v){
//以後の処理は省略
}
上記のコードの結果は下記の通り。
$ php -d vld.active=1 -d vld.execute=0 /path/to/dir/foreach.php
Finding entry points
Branch analysis from position: 0
2 jumps found. (Code = 77) Position 1 = 6, Position 2 = 9
Branch analysis from position: 6
2 jumps found. (Code = 78) Position 1 = 7, Position 2 = 9
Branch analysis from position: 7
1 jumps found. (Code = 42) Position 1 = 6
Branch analysis from position: 6
Branch analysis from position: 9
1 jumps found. (Code = 62) Position 1 = -2
Branch analysis from position: 9
filename: /path/to/dir/foreach.php
function name: (null)
number of ops: 11
compiled vars: !0 = $arr, !1 = $v, !2 = $i
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
2 0 E > INIT_FCALL 'range'
1 SEND_VAL 1
2 SEND_VAL 100000
3 DO_ICALL $3
4 ASSIGN !0, $3
3 5 > FE_RESET_R $5 !0, ->9
6 > > FE_FETCH_R ~6 $5, !1, ->9
7 > ASSIGN !2, ~6
8 > JMP ->6
9 > FE_FREE $5
6 10 > RETURN 1
branch: # 0; line: 2- 3; sop: 0; eop: 5; out0: 6; out1: 9
branch: # 6; line: 3- 3; sop: 6; eop: 6; out0: 7; out1: 9
branch: # 7; line: 3- 3; sop: 7; eop: 8; out0: 6
branch: # 9; line: 3- 6; sop: 9; eop: 10; out0: -2
path #1: 0, 6, 7, 6, 9,
path #2: 0, 6, 9,
path #3: 0, 9,
for文とforeach文ともに、配列のインデックス($i)と値($v)を参照できる状態にして、各々を確認するとforeachの方がコードが4行短くなっていた。
処理速度を確認してみると、for文の方が
php /path/to/dir/for.php 0.0024669170379639
で、foreach文の方が、
$ php /path/to/dir/foreach.php 0.0026559829711914
と大差なかった。
※処理速度の確認方法はPHPのVLDでfor文を見るに記載されている方法を採用している。
オペコードの行数が減るという視点から今回のようなケースではforeachの方が良いのかなと。
オペコードの行数がOPCacheにどのような影響を与えるか?を知る必要がありそうだ。





