- 本文地址: https://www.laruence.com/2009/04/07/670.html
- 转载请注明出处
你知道怎么写出最快的循环么?
刚刚在晓东郭的blog看到一个有趣的问题" PHP中 $i++ 和 ++$i 的区别 ":
方式一: $begin = time(); $i = 0; while(++$i < 10000) { $j = 0; while(++$j < 10000) ; ; } $end = time(); 时间 : 16s 方式二: $begin = time(); $i = 0; while($i < 10000) { $j = 0; while($j < 10000) ++$j; ++$i; } $end = time(); 时间:13s 方式三: $begin = time(); $i = 0; while($i < 10000) { $j = 0; while($j < 10000) $j++; $i++; } $end = time(); 时间:15s 方式四: $begin = time(); $i = 0; while($i++ < 10000) { $j = 0; while($j++ < 10000) ; ; } $end = time(); 时间:13s
呵呵, 为什么会这样呢?
对比第一种方法和第二种方法, 因为在PHP中, 最终被执行的是OPCODE, 每行opline都有俩个操作数, 对于操作数来说, 一般有3种类型的存取方式, 临时变量, 变量, 和编译时变量, 这三种变量其中, 存取最快的是第三种, 编译器变量, 在OpCode执行过程中, 会讲一个变量的加一级引用存储在一个hash结构中, 用来加快存取速度.
在第一种方法中:
$i = 0; while(++$i < 10000) { $j = 0; while(++$j < 10000) ; ; }
因为对于++$i来说, 我们需要得到它的返回值, 来和10000做比较, 这样就会使得PHP在编译的时候 , 生成一个变量(IS_VAR), 来保存自增的结果 , 也就是说, 这个时候用到了opline的return操作数.
然后, PHP会拿这个变量(IS_VAR)来和10000做比较.
而对于第二种方式:
$i = 0; while($i < 10000) { $j = 0; while($j < 10000) ++$j; ++$i; }
这个过程中, $i已经优化成了编译变量(IS_CV), 而对于++$i, 因为我们不需要保存他的返回值, 所以也只是直接对编译变量进行自增..
也就是说, 方法一和方法二的速度差异, 就在于 对于方式二, 我们一直都在实用编译变量.. 编译变量的存取速度远快于变量(IS_VAR)
再来看第三种和第四种方式:
//3: $i = 0; while($i < 10000) { $j = 0; while($j < 10000) $j++; $i++; } //4: $i = 0; while($i++ < 10000) { $j = 0; while($j++ < 10000) ; ; }
我们知道后缀自增(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掉这个临时变量....
[…] 本文地址: https://www.laruence.com/2009/04/07/670.html […]
第一种是不是循环的次数 和后面的不一样?版本5.5.18
哦,还真是第三个快啊,我在我的机子上跑这段代码,第一个用了5秒,第二个没试,直接式第三个,结果是用时0秒,看来还是需要弄懂PHP的工作原理呀。
我也同意用中文!!好容易找一个不是英文的站点
is this web site availible in English?
I’m sorry , no
是5.2.8
刚才说错了,第四种的POST_INC是有返回值存到临时变量里的,刚好用来比较,即
第四种是:
POST_INC !0 =>RES[~1]
IS_SMALLER ~1, 1000 =>RES[~2]
第二种是:
IS_SMALLER !0, 1000 =>RES[~1]
…
PRE_INC !0
第一种是:
PRE_INC !0 =>RES[$1]
IS_SMALLER $1, 1000 =>RES[~2]
四和二互有胜负就先不管了,理论上感觉二应该要比四快,但是我测了很多次结果第四种就没输过第一种,所有才有IS_TMP_VAR会不会比IS_VAR快的疑问,至于IS_CV,会不会它会做额外的工作,导致有时候反而不如一气呵成的IS_TMP_VAR呢?
IS_TMP_VAR和IS_CV都是从execute_data结构中获取的.
跟踪后发现,俩者的速度应该差别不是很大.都是一个类似数组结构的”cache”
刚才我测试了一下,测了5次,结果如下
9.7706120014191
9.0341799259186
10.309438943863
8.8307638168335
9.7692799568176
8.8732059001923
10.170838117599
8.8990998268127
9.7464888095856
8.8881361484528
10.337028026581
8.9553401470184
9.7715139389038
8.9210159778595
10.189666032791
8.8990459442139
9.7989208698273
9.0310311317444
10.350558996201
8.9781529903412
结果显示,第四种方法应该是最快的(或者说第四种和第二种并列第一),并不像laruence所说的比第一种慢,这意味着IS_TEM_VAR要比IS_VAR快?好像这是唯一的解释了…让我意外的是第四种竟然大多数情况下比第二种还要快,既然二四的前/后自增都不需要返回值,假定POST_INC和PRE_INC性能一样的话,那么就是说使用!进行IS_SMALLER运算的第二种跟使用~的第四种半斤八两?甚至,大多数情况下还要差?
哦? 从理论上来讲, 应该是IS_CV最快. 你的PHP版本是?
我在JAVA1.6下测试(不是JSP,不过应该差不多) JAVA要比PHP强悍,以亿次循环以毫秒为单位 说说测试结果吧(平均): 第一种:208MS 第二种:228MS 第三种:228MS 第四种:210MS
恩, JAVA是预编译么. 性能来讲JSP确实要比PHP来的快些
我觉得不是预编译的问题,因为随着循环次数的增加,PHP的消耗时间呈一次函数形式增长,如果存在预编译问题,哪么PHP循环一百次和JAVA循环时间应该差很大,但是PHP要快于JAVA
对了,问个问题,PHP预编译的时间会不计算在$start_time=time();$end_time=time();之中?
有理,是不会计算的.
还好一直用第二种
想请教个问题while的写法:
$i = 0;
while($i++ < 10000)
{
$j = 0;
while($j++ < 10000)
;
;
}
一般我都是这样写的:
$i = 0;
while($i++ < 10000)
{
$j = 0;
while($j++ < 10000){
;
}
;
}
省掉第二个while的{}有什么特别的意义么?
学习……
哈哈,Thanks.
其实这个也有论断,可以参考下这里 http://www.gracecode.com/archives/1426/
@明城, 恩, 本文的重点在于PHP特别性. 🙂
看球不懂~
@eve, …回家待着去….
@eve, 还是很简单明了的