奇技淫巧一:循环加速
循环通常是程序性能问题的多发地段。优化某些循环能极大的提高某段程序的性能。
以下的测试环境为:win xampp1.7.3 PHP5.3.1
【数组长度】
也许,maybe,可能,你看到如下类似的代码:
1 2 3 4 5 6 7 8 9 10 | <?PHP $data = range(0, 1000000); $start = xdebug_time_index(); for($i = 0; $i < count($data); $i++) { // 关注点在这行 } $end = xdebug_time_index(); echo $end - $start; |
以上的程序运行在我的电脑上基本上是3.3秒到3.4秒的样子。
如果我们把count($data)的计算提前到循环前,以局部变量在循环中迭代。如下所示代码:
1 2 3 4 5 6 7 8 9 10 11 | <?PHP $data = range(0, 1000000); $start = xdebug_time_index(); $len = count($data); // 其实局部变量是很快的。 for($i = 0; $i < $len; $i++) { } $end = xdebug_time_index(); echo $end - $start; |
在同样的环境下运行,基本上是0.32到0.4秒。
对比一下,整整一个数量级的提升。只是优化了一点,性能有如此大的提升。对于这样的操作我们需要在平时注意,养成习惯。虽然PHP内部取数组的长度是从Hash Table中直接取的count值。可是这个相对于局部变量,在性能上还是有较大差别的。
这里想到一个点,如果是HTML中使用JS取DOM集合,然后计算DOM的长度,此时如果有增加或删除节点,可能最后得到的结果并不是你要的那个结果。
【计数变量】
和数组长度一样,计数变量也是一个在循环中每次迭代都会使用的变量。如果我们优化这个点,此时性能上应该也有部分提高。如下所示代码:
1 2 3 4 5 6 7 8 9 10 | <?PHP $data = range(0, 1000000); $start = xdebug_time_index(); for($i = count($data); $i--;) { // 我是关注点所在行 } $end = xdebug_time_index(); echo $end - $start; |
以上的代码将计数变量从大到小递减,当为0时自动停止。从而将判断语句和计数加1两条语句变成了一条语句。
最后的运行时间大概为0.26到0.27秒。与前面的0.3到0.4秒,又有了大约30%的提升。
只是,只是这些是针对for语句,在PHP中我们还有foreach。
【foreach】
将上面的for语句换成foreach,如下所示代码:
1 2 3 4 5 6 7 8 9 | $data = range(0, 1000000); $start = xdebug_time_index(); foreach ($data as $row) { } $end = xdebug_time_index(); echo $end - $start; |
运行时间为:0.11~0.13秒,也许此时我们应该去内核里面看下为什么了,只是这不是今天的重点。嘿嘿
结果已经很明显了,如果可以,我们还是用foreach吧。
【循环展开】
在《编程珠玑》中介绍了这样一种方案。只是他是针对特定的数组查找的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | $data = range(0, 1000005); $start = xdebug_time_index(); $len = count($data); $left = $len % 8; $i = 0; while($left-- > 0) { $i++; } for(; $i < $len; $i+=8) { $t = $data[$i]; $t = $data[$i + 1]; $t = $data[$i + 2]; $t = $data[$i + 3]; $t = $data[$i + 4]; $t = $data[$i + 5]; $t = $data[$i + 6]; $t = $data[$i + 7]; } $end = xdebug_time_index(); echo $end - $start; |
以上的方法运行时间为0.3多一些,这和前面的方法类似,但是需要考虑到这里多了8个赋值语句。如果去掉这8个赋值语句,其时间为0.04秒。
以上的一些方法只是针对数量级比较大的数组,此时一些微小的优化都会产生巨大的能量。平时中,也许我们使用foreach就足够了。
【纠结的轮子】
最开始生成一百万的数组使用的是如下方式:
1 2 3 4 | $data = array(); for ($i = 0; $i < 1000005; $i++) { $data[$i] = $i; } |
纠结的轮子!