1.优化前后的对比
在进入一个循环之前计算长度是另一项可以优化的技术。以下代码是一个简单的for循环,它将遍历数组$item并分10次计算出数值,以确定哪些地方可以进行优化。代码清单1-28如下所示:
代码清单1-28 未优化的for循环
<?php $items = array(1,2,3,4,5,6,7,8,9,10); for($i=10;$i<count($items);$i++){ $x = 1999 * $i; }?>
我们主要注意for循环的逻辑,PHP按以下方式来执行此循环。
(1)初始化$i变量为0,从索引0开始循环,使用count()函数计算数组长度,$i计数器加1。
(2)迭代0完成后,开始索引1,使用count()计算数组长度,$i计数器加1。
(3)迭代1完成后,开始索引2,使用count()计算数组长度,$i计数器加1。
代码继续运行,直到到达数组元素的末尾。这段代码在开始时,问题便存在了。每次开始一个新索引的循环时,都必须调用函数count()来确定数组长度。上面的代码中,count()被调用了10次,其中有9次是多余的,因此这些不必要的调用需要替换,只要到达for循环之前调用一次count()就可以了。
那么修正优化后的for循环如代码清单1-29所示:
代码清单1-29 优化后的for循环
<?php $items = array(1,2,3,4,5,6,7,8,9,10); $count = count($items); for($i=10;$i<$count;$i++){ $x = 1999 * $i; }?>
上面的代码产生的结果与前一个代码清单的执行结果相同,但是函数的调用次数却从10次降到了1次,很明显的道理,调用的次数越少,PHP执行的程度就越快。
2.计算优化节省的时间
为了准确计算出减少9次count()函数调用能够节省多少时间,我们可以使用microtime()。在代码清单1-30中,增加另一个for循环,执行该代码10万次,代表有10万个用户来请求该脚本。我们对有变动的代码以粗体表示出来。
代码清单1-30 未优化的for循环基准代码
<?php $items = array(1,2,3,4,5,6,7,8,9,10); $start = microtime(); for($x = 0; $x<100000; $x++){ for($i=10;$i<count($items);$i++){ $x = 1999 * $i; } } echo microtime()-$start; ?>
执行10次该代码并计算结果的平均值,我们得出,10万次循环的总执行时间为0.046 ms。重新启动Web服务器,现在我们测试上面优化后的代码,如代码清单1-31所示:
代码清单1-31 优化后的for循环基准代码
<?php $items = array(1,2,3,4,5,6,7,8,9,10); $count = count($items); $start = microtime(); for($x = 0; $x<100000; $x++){ for($i=10;$i<$count;$i++){ $x = 1999 * $i; } } echo microtime()-$start; ?>
我们再次运行10次代码并获取平均值,使用此代码我们会看到,for循环的平均执行时间为0.0095 ms,减少了0.036 ms,即优化过的代码快了0.036 ms。
3.使用foreach替代for和while循环
访问数组数据的方法也是可以优化的,那就是尽量使用foreach语句,让它来替代while和for循环。
优化数据访问的方法对性能来讲很重要。许多Web应用需要从数据库、XML、JSON文件中读取数据并必须遍历每条记录后才能将数据显示给用户,能减少一毫秒等待,对于产品和用户来说,都有很重要的价值。
还是使用实例来说明这种优化之细节,如代码清单1-32所示:
代码清单1-32 使用foreach语句
<?php $items = array_fill(0,100000,’12345678910’); $start = microtime(); reset($items); foreach($items as $item) { $x = $item; } echo microtime()-$start; ?>
该脚本创建了一个数组$items,其中包含10万个元素,每个元素含有155个字节的字符串,代表数据库中典型数据。之后该代码设置了开始时间并使用foreach循环访问数组的每个元素,最后我们以毫秒为单位显示所用时间。我们连续执行了10次上面的代码清单,然后计算出每次执行时间的平均值,其结果为0.0078ms。
我们以上面的代码为基础,使用while循环,而不是foreach循环。代码清单粗体部分为我们对其做的修改,如代码清单1-33所示:
代码清单1-33 使用while循环
<?php $items = array_fill(0,100000,’12345678910’); $start = microtime(); reset($items); $i=0; while($i<100000){ $x = $items[$i]; $i++; } echo microtime()-$start; ?>
重新启动Web服务器,运行该代码10次以后,我们再次计算平均执行时间。用while循环访问数组中单个元素的平均时间为0.0099 ms。
下面我们再来比较一下使用for循环,如代码清单1-34所示。我们按照同样的基准循环流程,重启Web服务器,执行代码10次并计算平均结果。
代码清单1-34 使用for循环
<?php $items = array_fill(0,100000,’12345678910’); $start = microtime(); reset($items); $i=0; for($i=0;$i<100000;$i++){ $j = $items[$i]; } echo microtime()-$start; ?>
我们将上述3种循环基准的结果总结在表1-4中。
表1-4 10个元素数组的PHP循环平均执行时间
由此可见,使用foreach循环是访问数组元素性能最优之方法。感兴趣的朋友也可以自己来尝试一下。