【问题标题】:Why is foreach so slow?为什么foreach这么慢?
【发布时间】:2011-01-25 16:12:58
【问题描述】:

PHPBench.com 在每个页面加载时运行快速基准测试脚本。在 foreach 测试中,当我加载它时,foreach 的运行时间是第三个示例的 4 到 10 倍。

为什么母语结构明显比自己执行逻辑慢?

【问题讨论】:

  • 第三个测试有点奇怪:phpbench.com/source/test2/3 我宁愿自己尝试测试,看看是否出现任何错误(打开所有模式的错误报告)。
  • @Dor,我同意第三次测试有问题……但是什么?我似乎不明白为什么?该网站的作者提到 reset() 没有在 test2_3 中首先运行,但我一生都无法弄清楚为什么这很重要?
  • 我明白了。我知道怎么了!并不是他没有重置()数组,而是他没有将值设置回原来的值!请参阅下面的答案!

标签: php performance foreach


【解决方案1】:

也许这与 foreach 在数组的副本上工作有关?

或者也许它与这样一个事实有关,当使用 foreach 循环时,在每次迭代中,内部数组指针都会改变,指向下一个元素?

引用foreach's manual page 的相关部分:

注意:除非数组被引用, foreach 在副本上运行 指定数组而不是数组 本身。 foreach 有一些副作用 在数组指针上。


据我所知,您链接到的第三个测试没有做任何这两件事——这意味着两个测试不做同样的事情——这意味着你没有比较两种编写相同代码的方式.

(我还要说这种微优化在实际应用中根本不重要——但我想你已经知道了,只是出于好奇才问的) p>

在这个测试中还有一件事感觉不对:它只测试一次;;对于“更好”的测试,对所有这些进行多次测试可能会很有用——时间约为 100 微秒,不需要太多就能产生巨大的差异。
(考虑到第一次测试在几次刷新后会在 300% 和 500% 之间变化......)


对于那些不想点击的人,这是第一个测试(我得到了 3xx%、443% 和 529%)

foreach($aHash as $key=>$val) {
    $aHash[$key] .= "a";
}

第三个(100%)

$key = array_keys($aHash);
$size = sizeOf($key);
for ($i=0; $i<$size; $i++) {
    $aHash[$key[$i]] .= "a";
}

【讨论】:

  • 此外,非 foreach 不必设置变量值,然后在每个循环中从 $key$val 的变量中读取它们 - 它只是进行数组查找。
  • 然后第二个也做得非常好,尽管它似乎增加了指针。 (读者还要注意,这里的第一个百分比会有所不同,因为测试是在页面加载时重新运行的。)
  • 你读过 PHP 变量处理是如何工作的吗? derickrethans.nl/talks/phparch-php-variables-article.pdf
【解决方案2】:

对不起,网站弄错了。这是我自己的脚本,显示两者的速度几乎相同,实际上,foreach 更快!

<?php

function start(){
    global $aHash;
    // Initial Configuration
    $i   = 0;
    $tmp = '';
    while($i < 10000) {
      $tmp .= 'a';
      ++$i;
    }
    $aHash = array_fill(100000000000000000000000, 100, $tmp);
    unset($i, $tmp);
    reset($aHash);
}

/* The Test */
$t = microtime(true);
for($x = 0;$x<500;$x++){
    start();
    $key = array_keys($aHash);
    $size = sizeOf($key);
    for ($i=0; $i<$size; $i++) $aHash[$key[$i]] .= "a";
}
print (microtime(true) - $t);

print ('<br/>');

$t = microtime(true);
for($x = 0;$x<500;$x++){
    start();
    foreach($aHash as $key=>$val) $aHash[$key] .= "a";
}
print (microtime(true) - $t);
?>

如果您查看测试的源代码:http://www.phpbench.com/source/test2/1/http://www.phpbench.com/source/test2/3/,您可以看到 $aHash 在每次迭代后不会重新填充到初始数据中。它在开始时创建一次,然后每个测试运行 X 次。从这个意义上说,您正在为每次迭代使用不断增长的 $aHash... 在伪代码中:

iteration 1: $aHash[10000000000000]=='aaaaaa....10000 times...a';
iteration 2: $aHash[10000000000000]=='aaaaaa....10001 times...a';
iteration 2: $aHash[10000000000000]=='aaaaaa....10002 times...a';

随着时间的推移,所有测试的数据在每次迭代中都变得越来越大,因此当然到第 100 次迭代时,array_keys 方法会更快,因为它总是具有相同的 keys,其中foreach 循环必须应对不断增长的数据集并将 存储在数组中!

如果你在你的服务器上运行我上面提供的代码,你会清楚地看到 foreach 更快、更整洁、更清晰。

如果网站的作者打算让他的测试做它所做的事情,那么它肯定是不清楚的,否则,它就是一个无效的测试。

【讨论】:

  • 您在 array_fill() 中使用了 100000000000000000000000 的起始索引。这个数字对于php来说太大了,默默地调整为0。为什么要这样做?
【解决方案3】:

此类微观测量的基准结果来自一个活跃、繁忙的网络服务器,该服务器受到极端变化的负载和其他影响,应该被忽略。这不是一个可以作为基准的环境。

【讨论】:

  • 至少,不是为了精确。但似乎很明显,无论多么慢,foreach() 始终较慢。
  • 我很确定大多数人在他们的本地 PC 安装上运行此程序。虽然在性能上分心是浪费时间,但我们谈论的是 3 到 5 倍的速度提升,在某些情况下这很重要。
猜你喜欢
  • 2021-09-03
  • 2016-09-28
  • 2020-02-08
  • 2012-07-17
  • 2011-11-07
  • 2015-08-24
  • 2013-08-06
  • 2014-07-16
  • 2011-01-02
相关资源
最近更新 更多