· 作者: Laruence( )
· 本文地址: http://www.laruence.com/2009/04/07/670.html
· 转载请注明出处
你知道怎么写出最快的循环么?
刚刚在晓东郭的blog看到一个有趣的问题” PHP中 $i++ 和 ++$i 的区别 “:
1. 方式一:
2.
3. $begin = time();
4. $i = 0;
5. while(++$i < 10000)
6. {
7. $j = 0;
8. while(++$j < 10000)
9. ;
10. ;
11. }
12. $end = time();
13.
14. 时间 : 16s
15.
16. 方式二:
17.
18. $begin = time();
19. $i = 0;
20. while($i < 10000)
21. {
22. $j = 0;
23. while($j < 10000)
24. ++$j;
25. ++$i;
26. }
27. $end = time();
28.
29. 时间:13s
30.
31. 方式三:
32.
33. $begin = time();
34. $i = 0;
35. while($i < 10000)
36. {
37. $j = 0;
38. while($j < 10000)
39. $j++;
40. $i++;
41. }
42. $end = time();
43.
44. 时间:15s
45.
46. 方式四:
47.
48. $begin = time();
49. $i = 0;
50. while($i++ < 10000)
51. {
52. $j = 0;
53. while($j++ < 10000)
54. ;
55. ;
56. }
57. $end = time();
58. 时间:13s
呵呵, 为什么会这样呢?
对比第一种方法和第二种方法,因为在PHP中, 最终被执行的是OPCODE,每行opline都有俩个操作数, 对于操作数来说, 一般有3种类型的存取方式, 临时变量, 变量, 和编译时变量, 这三种变量其中, 存取最快的是第三种, 编译器变量, 在OpCode执行过程中, 会讲一个变量的加一级引用存储在一个hash结构中, 用来加快存取速度.
在第一种方法中:
1. $i = 0;
2. while(++$i < 10000)
3. {
4. $j = 0;
5. while(++$j < 10000)
6. ;
7. ;
8. }
因为对于++$i来说, 我们需要得到它的返回值, 来和10000做比较, 这样就会使得PHP在编译的时候 , 生成一个变量(IS_VAR), 来保存自增的结果 , 也就是说, 这个时候用到了opline的return操作数.
然后, PHP会拿这个变量(IS_VAR)来和10000做比较.
而对于第二种方式:
1. $i = 0;
2. while($i < 10000)
3. {
4. $j = 0;
5. while($j < 10000)
6. ++$j;
7. ++$i;
8. }
这个过程中, $i已经优化成了编译变量(IS_CV), 而对于++$i, 因为我们不需要保存他的返回值, 所以也只是直接对编译变量进行自增..
也就是说, 方法一和方法二的速度差异, 就在于对于方式二, 我们一直都在实用编译变量.. 编译变量的存取速度远快于变量(IS_VAR)
再来看第三种和第四种方式:
1. //3:
2. $i = 0;
3. while($i < 10000)
4. {
5. $j = 0;
6. while($j < 10000)
7. $j++;
8. $i++;
9. }
10.
11. //4:
12. $i = 0;
13. while($i++ < 10000)
14. {
15. $j = 0;
16. while($j++ < 10000)
17. ;
18. ;
19. }
我们知道后缀自增(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掉这个临时变量….