奇技淫巧一:循环加速

奇技淫巧一:循环加速
循环通常是程序性能问题的多发地段。优化某些循环能极大的提高某段程序的性能。
以下的测试环境为: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;
}

纠结的轮子!

奇技淫巧一:循环加速》上有8条评论

  1. 张二

    在PHP中,以下两种写法几乎没有性能上的差异,原因你懂的。
    =============方法一=============
    for($i = 0; $i < count($data); $i++) {}
    =======================
    =============方法二=============
    $len = count($data);
    for($i = 0; $i < $len; $i++) {}
    =======================

    回复
  2. 张二

    弄错了,应该是:
    在PHP中,以下两种写法几乎没有性能上的差异。
    =============方法一=============
    for($i = 0; $i < strlen($data); $i++) {}
    =======================
    =============方法二=============
    $len = strlen($data);
    for($i = 0; $i < $len; $i++) {}
    =======================

    回复
  3. Pingback引用通告: Thinking In LAMP Blog » Blog Archive » PHP每月通讯(2011年1月)

  4. Pingback引用通告: PHP的count与strlen实现 | Er

发表评论

电子邮件地址不会被公开。 必填项已用*标注


*

您可以使用这些HTML标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>