首页 文章

在PHP中FOR FOR FOR FORACH的性能

提问于
浏览
114

首先,我理解90%的应用程序中性能差异完全不相关,但我只需要知道哪个是更快的构造 . 那......

目前在网上提供的信息令人困惑 . 很多人说foreach很糟糕,但从技术上来说它应该更快,因为它假设使用迭代器简化了数组遍历的编写 . 迭代器,再次假设更快,但在PHP中也显然死得很慢(或者这不是PHP的东西?) . 我在谈论数组函数:next()prev()reset()等等,如果它们是偶数函数而不是那些看起来像函数的PHP语言特性之一 .

为了缩小这个范围:我对于任意点的遍历也不感兴趣,只有0到长度 . 我也不喜欢_1231147 .

以下是一些参考站点:
http://www.phpbench.com/
http://www.php.lt/benchmark/phpbench.php

我到处听到的内容:

  • foreach 很慢,因此 for / while 更快

  • PHPs foreach 复制它迭代的数组;为了使它更快你需要使用引用
    像这样的

  • 代码: $key = array_keys($aHash); $size = sizeOf($key); for ($i=0; $i < $size; $i++)foreach

这是我的问题 . 我写了这个测试脚本:http://pastebin.com/1ZgK07US并且不管我运行脚本多少次,我得到这样的东西:

foreach 1.1438131332397
foreach (using reference) 1.2919359207153
for 1.4262869358063
foreach (hash table) 1.5696921348572
for (hash table) 2.4778981208801

简而言之:

  • foreach 比带参考的 foreach

  • foreachfor
    对于哈希表,

  • foreachfor

谁能解释一下?

  • 我做错了吗?

  • PHP foreach引用的东西真的有所作为吗?我的意思是,如果你通过引用传递它为什么不复制它?

  • 什么's the equivalent iterator code for the foreach statement; I'在网上看到了一些,但每次我测试它们的时机都是关闭的;我还测试了一些简单的迭代器结构,但似乎从来没有得到过不错的结果 - PHP中的数组迭代器是不是很糟糕?

  • 是否有更快的方法/方法/构造来迭代除FOR / FOREACH(和WHILE)之外的数组?

PHP版本5.3.0


Edit: Answer 在这里的人们的帮助下,我能够拼凑出所有问题的答案 . 我在这里总结一下:

  • "Am I doing something wrong?"共识似乎是:是的,我可以看到echo是如何随机执行时间或任何其他函数如何以某种方式任何不同的函数 - 这和该脚本生成完全相同的结果的能力虽然只是"you're using echo"(我应该使用什么),但是比一切更好的foreach很难解释 . 但是,我承认测试应该用更好的东西来完成;虽然没有想到理想的妥协 .

  • "Is PHP foreach reference thing really making a difference? I mean why would it not copy it if you pass by reference?" ircmaxell显示是的,进一步的测试似乎证明在大多数情况下引用应该更快 - 虽然我给出了上面的代码片段,但绝对不是全部 . 我接受这个问题可能太不直观而无法在这样的水平上打扰并且需要一些极端的东西,例如反编译来实际确定哪种情况对每种情况都更好 .

  • "What's the equivalent iterator code for the foreach statement; I've seen a few on the net but each time I test them the timing is way off; I've also tested a few simple iterator constructs but never seem to get even decent results -- are the array iterators in PHP just awful?" ircmaxell提供了答案;虽然代码可能仅对PHP版本> = 5有效

  • "Are there faster ways/methods/constructs to iterate though a array other than FOR/FOREACH (and WHILE)?"谢谢戈登的回答 . 在PHP5中使用新的数据类型应该提供性能提升或内存提升(根据您的具体情况,可能需要其中任何一种) . 虽然速度方面许多新类型的数组似乎不比array()好,但splpriorityqueue和splobjectstorage似乎确实更快 . 链接由Gordon提供:http://matthewturland.com/2010/05/20/new-spl-features-in-php-5-3/

谢谢所有试图帮助的人 .

对于任何简单的遍历,我可能会坚持使用foreach(非参考版本) .

4 回答

  • 3

    我个人的意见是使用在上下文中有意义的东西 . 就个人而言,我几乎从不使用 for 进行数组遍历 . 我将它用于其他类型的迭代,但是 foreach 太简单了......在大多数情况下,时差差别很小 .

    值得关注的重点是:

    for ($i = 0; $i < count($array); $i++) {
    

    这是一个昂贵的循环,因为它会调用每次迭代 . 只要你不这样做,我认为这不重要......

    至于引用差异的参考,PHP使用copy-on-write,因此如果不写入数组,循环时的开销会相对较小 . 但是,如果您开始修改数组中的数组,那么您将开始看到它们之间的差异(因为需要复制整个数组,并且引用可以只修改内联)...

    至于迭代器, foreach 相当于:

    $it->rewind();
    while ($it->valid()) {
        $key = $it->key();     // If using the $key => $value syntax
        $value = $it->current();
    
        // Contents of loop in here
    
        $it->next();
    }
    

    就更快的迭代方式而言,它实际上取决于问题 . 但我真的需要问,为什么?我理解想要提高效率,但我认为你在浪费时间进行微观优化 . 记住, Premature Optimization Is The Root Of All Evil ......

    Edit: 根据评论,我决定快速进行基准测试......

    $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\n";
    
    $start = microtime(true);
    foreach ($a as $k => &$v) {
        $v = $v + 1;
    }
    echo "Completed in ", microtime(true) - $start, " Seconds\n";
    
    $start = microtime(true);
    foreach ($a as $k => $v) {}
    echo "Completed in ", microtime(true) - $start, " Seconds\n";
    
    $start = microtime(true);
    foreach ($a as $k => &$v) {}    
    echo "Completed in ", microtime(true) - $start, " Seconds\n";
    

    结果如下:

    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上)好像引用明显更快......

  • 26

    我不确定这是多么令人惊讶 . 大多数使用PHP编写代码的人都不熟悉PHP在裸机上的实际操作 . 我将陈述一些事情,大部分时间都是如此:

    • 如果您计算了're not modifying the variable, by-value is faster in PHP. This is because it'的参考值,那么按值计算则更少 . 它知道你修改ZVAL的第二个问题(PHP _1231183重要 . 引用使得更复杂,更多的簿记,它必须知道在修改变量时要做什么 . 所以,如果你最好不要与& . 我知道,它也是如此 .

    • Foreach并不慢 . 对于简单的迭代,它正在测试的条件 - “我是在这个数组的末尾” - 是使用本机代码而不是PHP操作码完成的 . 即使它是APC缓存的操作码,它仍然比在裸机上完成的一堆本机操作慢 .

    • 使用for循环“for($ i = 0; $ i <count($ x); $ i)因为count()而缺乏,并且缺乏PHP的能力(或实际上任何解释语言)来评估解析是否有任何东西修改数组 . 这可以防止它一次评估计数 .

    • 但是,即使你用“$ c = count($ x); for($ i = 0; $ i <$ c; $ i)修复它,$ i <$ c最多只是一堆Zend操作码,是$ i . 在100000次迭代过程中,这很重要.Toreach在本机级别知道该怎么做 . 没有PHP操作码需要测试“我在这个阵列结束时”的条件 .

    • 旧学校怎么样“while(列表(”东西?好吧,使用each(),current()等等都将涉及至少1个函数调用,这不是很慢,但不是免费的 . 是的,那些是PHP操作码再次!所以虽然列表每个都有它的成本 .

    由于这些原因,foreach可以理解为简单迭代的最佳选择 .

    不要忘记,它也是最容易阅读的,所以它是双赢的 .

  • 45

    在基准测试(尤其是phpbench.com)中需要注意的一点是,即使数字是合理的,测试也不是 . 很多关于phpbench.com的测试都是微不足道的,滥用PHP 's ability to cache array lookups to skew benchmarks or in the case of iterating over an array doesn' t实际上是在 real world 案例中测试它(没有人为循环写空) . 我发现它们反映了现实世界的结果,并且它们显示了该语言的原生迭代语法 foreach 出现在顶部(惊喜,惊喜) .

    //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)."\n"; //foreach 0.947947025299
    echo "while ".($end2 - $start2)."\n"; //while 0.847212076187
    echo "for ".($end3 - $start3)."\n"; //for 0.439476966858
    echo "foreach ref ".($end4 - $start4)."\n"; //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)."\n"; //for 0.397135972977
    echo "while ".($bend2 - $bstart2)."\n"; //while 0.364789962769
    echo "foreach ".($bend3 - $bstart3)."\n"; //foreach 0.346374034882
    
  • 95

    我想但我不确定: for 循环需要两个操作来检查和递增值 . foreach 将数据加载到内存中,然后它将迭代每个值 .

相关问题