Performance of FOR vs FOREACH in PHP
首先,我理解90%的应用程序中性能差异完全不相关,但我只需要知道哪个是更快的构造。那......
目前在网上提供的信息令人困惑。很多人说foreach很糟糕,但从技术上讲它应该更快,因为它假设使用迭代器简化了数组遍历的编写。迭代器,再次假设更快,但在PHP中也显然死得很慢(或者这不是PHP的东西?)。我在谈论数组函数:next()prev()reset()等等,如果它们是偶数函数而不是那些看起来像函数的PHP语言特性之一。
为了缩小这个范围:我在以超过1的步长遍历数组时没有兴趣(也没有负步骤,即反向迭代)。我也对从任意点到任意点的遍历不感兴趣,只有0到长度。我也没有看到定期操作超过1000个键的数组,但我确实看到一个数组在应用程序的逻辑中被遍历了多次!另外,对于操作,主要是字符串操作和回声。
以下是一些参考站点:
http://www.phpbench.com/
http://www.php.lt/benchmark/phpbench.php
我到处听到的内容:
-
foreach 很慢,因此for /while 更快 -
PHP
foreach 复制它迭代的数组;为了使它更快你需要使用引用 -
像这样的代码:
$key = array_keys($aHash); $size = sizeOf($key); 比
for ($i=0; $i < $size; $i++)foreach 快
这是我的问题。我写了这个测试脚本:http://pastebin.com/1ZgK07US,无论我运行脚本多少次,我得到这样的东西:
1 2 3 4 5 |
简而言之:
-
参考时,
foreach 比foreach 快 -
foreach 比for 快 -
对于哈希表,
foreach 比for 快
谁能解释一下?
PHP版本5.3.0
编辑:答案
在这里的人们的帮助下,我能够拼凑出所有问题的答案。我在这里总结一下:
谢谢所有试图帮助的人。
对于任何简单的遍历,我可能会坚持使用foreach(非参考版本)。
我个人的意见是使用在上下文中有意义的东西。就个人而言,我几乎从不使用
值得关注的重点是:
1 |
这是一个昂贵的循环,因为它会调用每次迭代。只要你不这样做,我认为这不重要......
至于引用差异的参考,PHP使用copy-on-write,因此如果不写入数组,循环时的开销会相对较小。但是,如果您开始修改数组中的数组,那么您将开始看到它们之间的差异(因为需要复制整个数组,并且引用只能修改内联)...
至于迭代器,
1 2 3 4 5 6 7 8 9 |
就更快的迭代方式而言,它实际上取决于问题。但我真的需要问,为什么?我理解想要提高效率,但我认为你在浪费时间进行微观优化。记住,
编辑:根据评论,我决定快速进行基准测试......
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 27 28 | $a = array(); for ($i = 0; $i < 10000; $i++) { $a[] = $i; } $start = microtime(true); foreach ($a as $k => $v) { $a[$k] = $v + 1; } echo"Completed in", microtime(true) - $start," Seconds "; $start = microtime(true); foreach ($a as $k => &$v) { $v = $v + 1; } echo"Completed in", microtime(true) - $start," Seconds "; $start = microtime(true); foreach ($a as $k => $v) {} echo"Completed in", microtime(true) - $start," Seconds "; $start = microtime(true); foreach ($a as $k => &$v) {} echo"Completed in", microtime(true) - $start," Seconds "; |
结果如下:
1 2 3 4 | Completed in 0.0073502063751221 Seconds Completed in 0.0019769668579102 Seconds Completed in 0.0011849403381348 Seconds Completed in 0.00111985206604 Seconds |
因此,如果您在循环中修改数组,使用引用会快几倍...
仅仅参考的开销实际上小于复制数组(这是在5.3.2)...所以它出现(至少在5.3.2上)好像引用明显更快......
我不确定这是多么令人惊讶。大多数使用PHP编写代码的人都不熟悉PHP在裸机上的实际操作。我将陈述一些事情,大部分时间都是如此:
如果您不修改变量,则PHP中的按值更快。这是因为无论如何都要计算它的参考值,并且按值给它做的更少。它知道第二个你修改ZVAL(PHP的大多数类型的内部数据结构),它必须以一种简单的方式将其分解(复制它并忘记其他ZVAL)。但你永远不会修改它,所以没关系。引用使得更复杂,更多的簿记,它必须知道在修改变量时要做什么。所以,如果你是只读的,那么矛盾的是,最好不要用&。我知道,这是违反直觉的,但也是如此。
Foreach并不慢。对于简单的迭代,它正在测试的条件 -"我是在这个数组的末尾" - 是使用本机代码而不是PHP操作码完成的。即使它是APC缓存的操作码,它仍然比在裸机上完成的一堆本机操作慢。
使用for循环"for($ i = 0; $ i
但即使你用"$ c = count($ x); for($ i = 0; $ i <$ c; $ i ++)修复它,$ i <$ c也是一堆Zend操作码最多,因为$ 100 ++。在100000次迭代过程中,这很重要.Toreach在本机级别知道该怎么做。没有PHP操作码需要测试"我在这个阵列结束时"的条件。
旧学校怎么样"while(列表("东西?好吧,使用each(),current()等等)都将涉及至少1个函数调用,这不是很慢,而是不是免费的。是的,那些再次是PHP操作码!所以虽然+ list +每个都有它的成本。
由于这些原因,foreach可以理解为简单迭代的最佳选择。
不要忘记,它也是最容易阅读的,所以它是双赢的。
在基准测试(尤其是phpbench.com)中需要注意的一点是,即使数字是合理的,测试也不是。很多关于phpbench.com的测试都是微不足道的,并且滥用PHP缓存数组查找的能力来歪斜基准测试,或者在迭代数组的情况下实际上并没有在实际情况下测试它(没有人写空循环)。我已经完成了自己的基准测试,我发现这些基准测试反映了现实世界的结果,并且它们总是显示语言的本机迭代语法
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 | //make a nicely random array $aHash1 = range( 0, 999999 ); $aHash2 = range( 0, 999999 ); shuffle( $aHash1 ); shuffle( $aHash2 ); $aHash = array_combine( $aHash1, $aHash2 ); $start1 = microtime(true); foreach($aHash as $key=>$val) $aHash[$key]++; $end1 = microtime(true); $start2 = microtime(true); while(list($key) = each($aHash)) $aHash[$key]++; $end2 = microtime(true); $start3 = microtime(true); $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i<$size; $i++) $aHash[$key[$i]]++; $end3 = microtime(true); $start4 = microtime(true); foreach($aHash as &$val) $val++; $end4 = microtime(true); echo"foreach".($end1 - $start1)." "; //foreach 0.947947025299 echo"while".($end2 - $start2)." "; //while 0.847212076187 echo"for".($end3 - $start3)." "; //for 0.439476966858 echo"foreach ref".($end4 - $start4)." "; //foreach ref 0.0886030197144 //For these tests we MUST do an array lookup, //since that is normally the *point* of iteration //i'm also calling noop on it so that PHP doesn't //optimize out the loopup. function noop( $value ) {} //Create an array of increasing indexes, w/ random values $bHash = range( 0, 999999 ); shuffle( $bHash ); $bstart1 = microtime(true); for($i = 0; $i < 1000000; ++$i) noop( $bHash[$i] ); $bend1 = microtime(true); $bstart2 = microtime(true); $i = 0; while($i < 1000000) { noop( $bHash[$i] ); ++$i; } $bend2 = microtime(true); $bstart3 = microtime(true); foreach( $bHash as $value ) { noop( $value ); } $bend3 = microtime(true); echo"for".($bend1 - $bstart1)." "; //for 0.397135972977 echo"while".($bend2 - $bstart2)." "; //while 0.364789962769 echo"foreach".($bend3 - $bstart3)." "; //foreach 0.346374034882 |
我想但我不确定: