はじめに
今回はfor文に関するtipsについて解説していきます。
for文の条件式にはcount($array)のような関数をいれない(変数に格納)
for文の条件式に関数を入れると遅くなるというtipsです。
例えば配列の要素数だけfor文を用いてループを回したいときに、
for ($j=0; $j<count($array); $j++) {}
このtipsでは下記のようにあらかじめ変数に格納してからの使用を勧めてます。
$count = count($array);
for ($j=0; $j<$count; $j++) {}
まずはサンプルプログラムを用意し、
<?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);
?>
<?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
まずは、
$ 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.
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
実行結果は上記のようになりました。それでは、
2:DO_
このようにforループが続く間、
次に、
191 | T_FOR
192 '('
193 for_expr
194 ';' { zend_do_free(&$3 TSRMLS_CC); $4.u.opline_num = get_next_op_number(CG(active_op_array)); }
195 for_expr
196 ';' { zend_do_extended_info(TSRMLS_C); zend_do_for_cond(&$6, &$7 TSRMLS_CC); }
197 for_expr
198 ')' { zend_do_free(&$9 TSRMLS_CC); zend_do_for_before_statement(&$4, &$7 TSRMLS_CC); }
199 for_statement { zend_do_for_end(&$7 TSRMLS_CC); }
上記はphp-5.
“for(A;B;C){D}”
続いて、
1548 void zend_do_end_function_call(znode *function_name, znode *result, znode *argument_list, int is_method, in t is_dynamic_fcall TSRMLS_DC)
1549 {
1550 zend_op *opline;
1551
1552 if (is_method && function_name && function_name->op_type == IS_UNUSED) {
1553 /* clone */
1554 if (Z_LVAL(argument_list->u.constant) != 0) {
1555 zend_error(E_WARNING, "Clone method does not require arguments");
1556 }
1557 opline = &CG(active_op_array)->opcodes[Z_LVAL(function_name->u.constant)];
1558 } else {
1559 opline = get_next_op(CG(active_op_array) TSRMLS_CC);
1560 if (!is_method && !is_dynamic_fcall && function_name->op_type==IS_CONST) {
1561 opline->opcode = ZEND_DO_FCALL;
1562 opline->op1 = *function_name;
1563 } else {
1564 opline->opcode = ZEND_DO_FCALL_BY_NAME;
1565 SET_UNUSED(opline->op1);
1566 }
1567 }
zend_
736 void zend_do_for_cond(znode *expr, znode *second_semicolon_token TSRMLS_DC)
737 {
738 int for_cond_op_number = get_next_op_number(CG(active_op_array));
739 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
740
741 opline->opcode = ZEND_JMPZNZ;
742 opline->op1 = *expr; /* the conditional expression */
743 second_semicolon_token->u.opline_num = for_cond_op_number;
744 SET_UNUSED(opline->op2);
745 }
zend_
748 void zend_do_for_before_statement(znode *cond_start, znode *second_semicolon_token TSRMLS_DC)
749 {
750 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
751
752 opline->opcode = ZEND_JMP;
753 opline->op1.u.opline_num = cond_start->u.opline_num;
754 CG(active_op_array)->opcodes[second_semicolon_token->u.opline_num].extended_value = get_next_op_number( CG(active_op_array));
755 SET_UNUSED(opline->op1);
756 SET_UNUSED(opline->op2);
757
758 do_begin_loop(TSRMLS_C);
759
760 INC_BPC(CG(active_op_array));
761 }
zend_
764 void zend_do_for_end(znode *second_semicolon_token TSRMLS_DC)
765 {
766 zend_op *opline = get_next_op(CG(active_op_array) TSRMLS_CC);
767
768 opline->opcode = ZEND_JMP;
769 opline->op1.u.opline_num = second_semicolon_token->u.opline_num+1;
770 CG(active_op_array)->opcodes[second_semicolon_token->u.opline_num].op2.u.opline_num = get_next_op_numbe r(CG(active_op_array));
771 SET_UNUSED(opline->op1);
772 SET_UNUSED(opline->op2);
773
774 do_end_loop(second_semicolon_token->u.opline_num+1, 0 TSRMLS_CC);
775
776 DEC_BPC(CG(active_op_array));
777 }
zend_
このように、
話がだいぶずれてしまいましたが、
おわりに
これまで4回にわたってお送りした
本連載をきっかけにさらにPHPに興味を持っていただけたら幸いです。