$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にどのような影響を与えるか?を知る必要がありそうだ。