【问题标题】:Is there a maximum amount of iterations PHP can loop?PHP 可以循环的最大迭代次数是多少?
【发布时间】:2017-11-28 02:27:14
【问题描述】:

在正确的配置、一个非常简单的过程和足够的内存的情况下,PHP 是否对循环中的迭代次数有上限,或者它会继续执行直到您耗尽内存、达到最大执行时间或出错以其他方式?

如果是这样,不同类型的循环是否有所不同? while, do...while, for, foreach?

例如:

<?php 
for ($i=0; $i<999999999999999999; $i++) {
  echo "$i<br/>";
};

我意识到这有点荒谬,但这主要是我对 PHP 限制的好奇。我在 SO 上讨论的文档或这个特定问题中找不到任何内容。

我确实在本地开发环境中运行了这个脚本,该环境运行 PHP 7.1 内置服务器,运行频率为 3.1GHz 英特尔酷睿 i7 MacBook Pro,内存为 16GB,max_execution_time 为零。在大约 720,000 之后,浏览器本身将达到它自己的限制并冻结。我确实通过 CLI 运行了同样的测试,虽然它花了很长时间,但它确实执行并运行了,但我最终手动停止了它。

【问题讨论】:

  • 你有几件事涉及 - 最大缓冲区大小(你的回声),最大 int 大小(你可以做 while(true) 不关心那个)......
  • 代码中的限制是 999999999999999999,尽管 while(true) 可以永远迭代。
  • 循环不使用任何内存;您遇到的限制位于 Web 服务器/网络/浏览器堆栈的其他位置。
  • 将浏览器排除在外。运行脚本服务器端,结束了吗?
  • @chris85 现在运行它,它运行得很好,没有尽头,尽管我正在给我的粉丝一个很好的锻炼。

标签: php loops


【解决方案1】:

浏览器将冻结,因为它不会得到响应,除非它得到 500 或任何其他错误状态/响应。从理论上讲,PHP 可以无限期地运行。

例如,我使用这样的东西在后台处理一些“工作”:

while(true) {
    // and I do some wonders here
    // if some conditions are met
}

当涉及到您的浏览器时,是的 - 它会在等待 200 OK 响应代码时冻结,由于 Web 服务器 TimeOut 配置、PHP 的 max_execution_time 设置(和其他),肯定不会到达...和许多其他因素,更不用说传输到浏览器的数据大小了,在您从0999999999999999999 迭代和回显每个数字的情况下,这将导致浏览器的实际“崩溃”。

您的for 循环将到达它的目的地999999999999999999(可能不在您的浏览器中,但肯定在终端中),除非有其他东西停止或破坏它(即 CTRL+C、条件、max_execution_time等)。

【讨论】:

  • “你的 for 循环将到达它的目的地 999999999999999999(可能不在你的浏览器中,但肯定在终端中......”。有问题的数字大约是 10^18;如果计算机需要每次增量 1 纳秒(例如,在 1 GHz 内核上运行的每个增量 1 个周期),这已经是 10^9 秒 - 大约 30 年。
  • 实际上,浏览器会冻结,因为服务器会向它发送数据,但服务器不会有结束的“200 OK”响应——可能永远不会。我不知道我说错了什么,也不知道我是怎么糊涂的?请详细说明。 (英语不是我的主要)
  • 要评论@Kevin 所说的,我只是对现代 8 核 5GHz CPU 进行了快速计算,并且可以制作一个在大约 300 天内大致迭代此大小循环的程序。在大多数情况下仍然完全无法使用。
【解决方案2】:

tl;博士

假设 PHP 有足够的资源并且进程没有被操作系统、Apache、用户等终止,PHP 将很乐意永远循环......


您将浏览器问题与 PHP 混淆了。浏览器无法处理这么多echo "$i&lt;br/&gt;"; 的呈现,因此它崩溃或冻结或其他任何情况。也有可能是 PHP 在将其发送到浏览器之前将这么多 $i 放入输出缓冲区而导致内存不足。

尝试正确地对代码进行基准测试,您将很快得到答案:

// Let's set the start timer
$start = microtime(true);

// Add or remove 9's as you please
for($i=0; $i<999999999999999999; ++$i){}

// How many seconds did this take to complete?
echo (microtime(true) - $start).' seconds<br/>';

// What was the peak memory usage during this code execution?
echo memory_get_peak_usage(true).' bytes used';

假设 Apache/PHP/您的浏览器没有超时,那么您应该会看到结果;如果没有,则转到命令行。

【讨论】:

  • 谢谢。我不会说我将浏览器与 PHP 混淆了。我知道浏览器在 PHP 之前达到了它自己的限制,这只是我运行的第一个测试。
  • @RobertWade 我的错;代码示例导致我得出错误的结论。无论如何,我希望我的例子有所帮助。另外,请注意 ++$i$i++ :)
  • 所以预先增加索引将防止输出缓冲区限制?
  • @RobertWade 不,只是微优化导致$i 增加得更快。在现实世界中并不是很明显,但是如果您有一个具有数百万次迭代的操作,那么它会减少 30% 或更多的执行时间,具体取决于 PHP 版本。
  • 谢谢。很高兴知道这一点。
【解决方案3】:

999999999999999999 超出了可以表示的最大安全整数。如果你问 PHP 9007199254740992 + 1 多少钱,它会告诉你它是9007199254740992。换句话说:

<?php var_dump ((9007199254740992 + 1) === 9007199254740992);

输出bool(true)

这是浮点数的限制。因此,您的迭代变量$i 将不会达到999999999999999999。您已经有效地创建了一个无限循环。

您可以考虑9007199254740992 的限制。当然,这是因为 PHP 用于变量 (double, a.k.a IEEE 754 binary64) 的数据类型。


如果您使用无限循环并尝试计算迭代次数,您将遇到同样的问题。理论上你可以使用一个可以处理任意大整数的库……但是 PHP 没有理由停止,至少在硬件开始出现故障之前不会停止。

琐事:在100 gigaflop/second 执行的999999999999999999 flop 需要115 days 17 hours 46 minutes 40 seconds 才能运行。


勘误表:在 PHP 的 64 位构建中,可以表示的最大安全整数是 9223372036854775807。换句话说:

<?php var_dump ((9223372036854775808 + 1) === 9223372036854775808);

输出bool(true)

Try online.

9223372036854775808 大约比999999999999999999 大一个数量级,因此这个循环在 PHP 的 64 位构建中不是无限的。这不是quad precision。经过进一步测试,发现 PHP 使用了一个 64 位整数,直到 9223372036854775807,然后切换到双精度:

<?php
var_dump (serialize(9223372036854775807));
var_dump (serialize(9223372036854775808));

输出

string(22) "i:9223372036854775807;"
string(25) "d:9.2233720368547758E+18;"

Try online.

同时 32 位构建将使用 32 位整数到 2147483647,然后使用 switch 来加倍。

【讨论】:

  • 这是否特定于某个 P​​HP 版本或 x86 与 x64?如果不是,那么在sandbox.onlinephpfunctions.com 测试&lt;?php var_dump ((9007199254740992 + 1) === 9007199254740992); 必须是纯巫术。
  • @MonkeyZeus 原来你是对的,我没有在 64 位版本上测试过,添加了勘误表。
  • 非常有趣的答案!老实说,在今天之前我并不知道这个 PHP 的复杂性,所以我很高兴你发布了这个答案:)
  • 999999999999999999 只是变成了一个浮点数 (var_dump(999999999999999999) -&gt; float(1.0E+18)),我不明白为什么它会创建一个无限循环。
  • @SalmanA This video 使用《超级马里奥 64》用花车来演示这个问题,我发现它非常平易近人。
猜你喜欢
  • 2020-02-16
  • 1970-01-01
  • 2014-10-11
  • 2020-02-02
  • 2021-07-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-05-19
相关资源
最近更新 更多