Posted by & filed under 每日一题.

【四脚猫】每日一题(10月14日):有趣的 $i++ 与 ++$i

 

  1. 方式一:
  2. $begin = time();
  3. $i = 0;
  4. while(++$i < 10000)
  5. {
  6.   $j = 0;
  7.   while(++$j < 10000)
  8.     ;
  9.   ;
  10. }
  11. $end = time();
  12. 时间 : 16s
  13. 方式二:
  14. $begin = time();
  15. $i = 0;
  16. while($i < 10000)
  17. {
  18.   $j = 0;
  19.   while($j < 10000)
  20.     ++$j;
  21.   ++$i;
  22. }
  23. $end = time();
  24. 时间:13s
  25. 方式三:
  26. $begin = time();
  27. $i = 0;
  28. while($i < 10000)
  29. {
  30.   $j = 0;
  31.   while($j < 10000)
  32.     $j++;
  33.   $i++;
  34. }
  35. $end = time();
  36. 时间:15s
  37. 方式四:
  38. $begin = time();
  39. $i = 0;
  40. while($i++ < 10000)
  41. {
  42.   $j = 0;
  43.   while($j++ < 10000)
  44.     ;
  45.   ;
  46. }
  47. $end = time();
  48. 时间:13s

这是为啥?

 

参考答案:

评论区中有同学说不同的方式中少了10000次循环,才导致的快, 这个说法有一定道理,如果你在少循环的方法中再增加10000次循环, 其实该快的还是快,该慢的还是慢.下面做一下说明

 

首先 , 在PHP中, 最终被执行的是OPCODE, 每行opline都有俩个操作数, 对于操作数来说, 一般有3种类型的存取方式, 临时变量, 变量, 和编译时变量, 这三种变量其中, 存取最快的是第三种, 编译器变量, 在OpCode执行过程中, 会将一个变量的加一级引用存储在一个hash结构中, 用来加快存取速度.

在第一种方法中:因为对于++$i来说, 我们需要得到它的返回值, 来和10000做比较, 这样就会使得PHP在编译的时候 , 生成一个变量(IS_VAR), 来保存自增的结果 , 也就是说, 这个时候用到了opline的return操作数.
然后, PHP会拿这个变量(IS_VAR)来和10000做比较.

 

第二种方法中:这个过程中, $i已经优化成了编译变量(IS_CV), 而对于++$i, 因为我们不需要保存他的返回值, 所以也只是直接对编译变量进行自增..也就是说, 方法一和方法二的速度差异, 就在于 对于方式二, 我们一直都在实用编译变量.. 编译变量的存取速度远快于变量(IS_VAR)

 

对于第三种和第四种:

我们知道后缀自增(POST_INC), 会返回一个对原值的copy, 然后自增.
对于第四种方式, $i++以后, ZE会将$i的原值, 存储在一个临时变量(IS_TMP_VAR). 并且会拿这个临时变量和10000对比.
所以, 严格来讲, 这部分的速度比起第一种方式来说是会慢一些的.

而第三种方式慢在, 因为对$i++的返回值没有使用, 在语法分析阶段, $j++会归约成(rw_variable T_INC->expr_without_variable->expr, expr+’;’ => zend_do_free). 所以ZE就会安排一条opline, free掉这个临时变量….

欢迎各位攻城狮,各位大牛给每日一题投稿,大家可以把自己碰到的有趣问题,工作中碰到的难题等…发送到 稿件邮箱:2313427189@qq.com