【问题标题】:PHP why is continue n slower than using breakPHP为什么继续n比使用break慢
【发布时间】:2011-04-02 21:49:37
【问题描述】:

请考虑以下代码:

$start = microtime();
for($i = 2; $i < 100; $i++)
{
    for($y = 2; $y <= sqrt($i); $y++)
    {
        if($i%$y != 0)
        {
            continue;
        }
        else
        {
          continue 2;
        }
    }

    echo $i.',';
}
echo "\nFinished in " . (microtime() - $start);

鉴于上述代码有效地使用 continue 2 来中断内循环并跳过内循环后的任何代码,为什么以下代码在执行更多操作时平均执行得更快:

 $start = microtime();
for($i = 2; $i < 100; $i++)
{
    $flag = true;
    for($y = 2; $y <= sqrt($i); $y++)
    {
        if($i%$y != 0)
        {
            continue;
        }
        else
        {
          $flag = false;
          break;
        }
    }

    if($flag === true) echo $i.',';
}
echo "\nFinished in " . (microtime() - $start);

感谢您的任何意见。

_____ 更新____________

感谢您的反馈,但我们似乎没有抓住重点。不管这是否是一种好的编程实践,我都试图理解为什么性能差异(很小但一致)不在我预期的偏差范围内。

true 到 microtime 的传递似乎无关紧要,因为两个样本都是使用相同的方法、相同的开销和相同的不准确性进行测量的。

测试了不止一次运行,正如使用“平均”一词所暗示的那样。

仅为了说明,请考虑以下使用 microtime(true) 的小样本,其显示与使用 microtime() 相同的模式。

我知道这是一个小样本,但模式很清楚:

继续 0.00037288665771484 0.00048208236694336 0.00046110153198242 0.00039386749267578 0.0003662109375

休息 0.00033903121948242 0.00035715103149414 0.00033307075500488 0.00034403800964355 0.00032901763916016

感谢您的关注,并感谢您提供任何进一步的反馈。

______ 更新进一步调查____________

有趣的是,如果将 echo 语句从代码中删除,则 continue 执行速度会更快,而使用 echo 语句时 break 会更快。

请考虑以下代码示例,并考虑结果是否冲突取决于是否删除了 echo 语句:

<?php
$breakStats = array();
$continueStats = array();

ob_start();
for($i = 0; $i < 10000; $i++)
{
   $breakStats[] = doBreakTest();
   $continueStats[] = doContinueTest();
}
ob_clean();

echo "<br/>Continue Mean " . (array_sum($continueStats) / count($continueStats));
echo "<br/>Break Mean " . (array_sum($breakStats) / count($breakStats));

function doBreakTest()
{
    $start = microtime(true);
    for($i = 2; $i < 100; $i++)
    {
        $flag = true;
        $root = sqrt($i);
        for($y = 2; $y <= $root; $y++)
        {
            if($i%$y != 0)
            {
                continue;
            }
            else
            {
                $flag = false;
                break;
            }
        }
    }

    if($flag === true) echo $i . '';
    return microtime(true) - $start;
}

function doContinueTest()
{
    $start = microtime(true);
    for($i = 2; $i < 100; $i++)
    {
        $root = sqrt($i);
        for($y = 2; $y <= $root; $y++)
        {
            if($i%$y != 0)
            {
                continue;
            }
            else
            {
                echo $i . '';
                continue 2;
            }
        }
    }
    return microtime(true) - $start;
}

存在 Echo 语句:

继续平均 0.00014134283065796 断裂均值 0.00012669243812561

Echo 语句不存在:

继续平均 0.00011746988296509 断裂均值 0.00013022310733795

请注意,通过从 break 和 flag 测试中删除 echo 语句,我们也删除了 ($flag === true) 检查,因此负载应该会减少,但在这种情况下继续仍然会获胜。 W

所以在纯粹的 continue n 与 break + flag 场景中,似乎 continue n 是更快的构造。但是添加相同数量的相同 echo 语句,以及 continue n 性能标志。

从逻辑上讲,continue n 应该更快,这对我来说是有道理的,但我希望看到存在的 echo 语句也是如此。

这显然是生成的操作码的差异,并且 echo 语句的位置(内循环与外循环)有人知道查看生成的操作码的方法吗?我想这是我在试图了解内部发生的事情时所需要的最终结果。

谢谢:)

【问题讨论】:

  • 输出是否相同?你能把它们贴在这里吗?
  • 嗯,快多少?你应该使用microtime(TRUE) 来获得一个对比较有用的浮点数。
  • single 运行进行基准测试毫无意义。运行 10,000 个脚本并取平均值以获得有用的数据。然后考虑它到底有多快(如果有的话),以及它对你的真正重要性。
  • 您应该避免在循环中过度使用continuebreak。考虑改用while。不要在 for-loop 的测试表达式中使用函数。
  • 它们在 PHP 5.3.6 上的执行时间几乎相同。虽然它是一个微基准,所以无论您遇到什么不同,都取决于它是如何在 PHP 内部实现的。

标签: php break continue


【解决方案1】:

是的,第一个更快一点。这是因为它只是在 continue 2 上跳出并打印 $i。

第二个例子还有更多的工作要做……给 $flag 变量赋值,跳出循环,检查 $flag 的值,检查 $flag 的类型(也比较),然后打印出 $i。它有点慢(简单的逻辑)。

不管怎样,有什么目的吗?

我的一些比较结果

0.0011570 < 0.0012173
0.0011540 < 0.0011754
0.0011820 < 0.0012036
0.0011570 < 0.0011693
0.0011970 < 0.0012790

已使用:PHP 5.3.5@@Windows(1000 次尝试;100% 先更快)

0.0011570 < 0.0012173
0.0005000 > 0.0003333
0.0005110 > 0.0004159
0.0003900 < 0.0014029
0.0003950 > 0.0003119
0.0003120 > 0.0002370

已使用:PHP 5.3.3@@Linux(1000 次尝试;32% 第一:68% 第二更快)

0.0006700 > 0.0004863
0.0003470 > 0.0002591
0.0005360 > 0.0004027
0.0004720 > 0.0004229
0.0005300 > 0.0004366

已使用:PHP 5.2.13@@Linux(1000 次尝试;先 9% :91% 后更快)

抱歉,我没有更多的服务器来测试 :) 现在我认为这主要取决于硬件(也可能取决于操作系统)。

一般来说:仅证明Linux服务器比在Windows上运行更快:)

【讨论】:

  • 也许你是对的,但问题表明,第一个是较慢,而不是更快。
  • @KingCrunch:也许吧,但我把那个代码放到了测试中,每次运行第一个都更快。
  • @Wh1T3h4Ck5 有趣的观察,请问什么平台和PHP版本?
  • 这可能特定于我的确切硬件,逻辑上我和你在一起,因此问题,但这些不是我看到的结果。我会尝试另一个盒子。
  • @Gavin:这是给你的伙伴(10,000 次尝试 x 3 台服务器)。你是对的,第二个更快......更快。 service-kl.com/code/results.zip 我上次测试的完整结果,用 microtime(true) 改进了一个。
【解决方案2】:

continue 2 版本对我来说稍快一些。但这些不是您通常需要关心的事情类型。考虑:

for($y = 2; $y <= sqrt($i); $y++)

在这里,您正在计算每次迭代的sqrt。只需将其更改为:

$sqrt = sqrt($i);
for($y = 2; $y <= $sqrt; $y++)

比在两个几乎相同的循环样式之间切换会给您带来更好的改进。

如果您发现更容易理解,则应使用continue 2。计算机并不关心。

要解决有关查看操作码的更新,请参阅:

php -d vld.active=1 -d vld.execute=0 -f foo.php

【讨论】:

  • 优秀的点参考代码可读性和从初始化程序中取出计算。把这个放在上下文中,我们并不是真的在寻找最快的方法来实现这一点。我们正在讨论面试问题的答案,在讨论中我建议 continue 2 方法的性能会比 break 版本稍微好一些。我们测试了它,在我的硬件和服务器上,我错了。我只是不明白为什么。
  • @Gavin,如果您不讨论最快的方法,那么甚至不会问这个问题。 ;) 我理解这种好奇心,但方法是如此相似,几乎不可能以任何有意义的方式对其进行基准测试,尤其是当它甚至还没有接近成为瓶颈时。更好的测试(但基本上仍然没有用)是不包含 echo 语句的测试。
  • @Gavin,如果您想比较操作码,请参阅pecl.php.net/package/vld。我认为这不会有太大帮助。我认为了解 Zend 引擎的工作原理以及现代编译器 + 计算机如何优化和进行预测会更有用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-06-29
  • 1970-01-01
  • 2011-03-23
  • 2020-06-26
  • 2014-07-17
  • 2014-08-19
  • 1970-01-01
相关资源
最近更新 更多