包含PHP文件对性能有多大影响?

5
问题已经很清楚了,我正在处理一个大型项目,在其中大多数对php的调用include()在100到150个文件之间。平均而言,php所花费的时间在150到300毫秒之间。我想知道这其中有多少是由于包含PHP脚本引起的?我一直在考虑运行一个检查最常访问文件的脚本,并将它们合并成一个文件以加快速度,但我不知道这是否会产生任何影响。
我应该注意到我使用APC,我不完全了解APC在后台做什么,但我想它可能已经以某种方式缓存了我的文件,因此文件数量并不会产生太大的影响?
非常感谢任何关于此主题的意见。
当然,300毫秒并不算太多,但如果我能将其降低到100甚至50毫秒,那将是一个显著的提升。
编辑:
为了澄清,我说的是通过php include / require加载文件。

使用xdebug生成分析器输出,并在cachegrind中查看,以了解时间花费在哪里。值得一提的是,几个PHP库分发单文件版本(NBBC和HTMLpurifier是其中之一),因此似乎有证据表明合并文件可能是值得的。顺便说一句,APC可以告诉您哪些文件被最频繁访问。使用捆绑的apc.php脚本检查系统缓存。 - Frank Farmer
硬盘性能可能与此无关:首先,他正在使用APC。其次,大多数操作系统在操作系统级别上提供文件系统缓存。重复读取访问实际上并不经常命中磁盘。 - Frank Farmer
1
感谢所有的回复。让我澄清一下,通过文件访问,我是指由“require”和“include”语句打开的文件。我的意思是,与从数量明显较少的文件运行完全相同的代码相比,它是否对性能产生影响。 - Naatan
尝试在编辑中反映这一点。请纠正我任何错误。 - Frank Farmer
谢谢,我在发布问题时没有考虑到它可能被解释的方式。 - Naatan
显示剩余3条评论
3个回答

11

文件加载是个棘手的问题。正如其他人所说,唯一确定的方法是进行一些基准测试。但是,以下是一些一般规则仅适用于PHP加载,不适用于使用fopen打开文件:

  • APC将其opcode缓存存储在共享内存中,因此您将在第一次加载时遭受打击,而在随后的加载中则不会。
  • includeinclude_once(及其require近亲)实际上非常重。以下是一些提高它们速度的技巧:
    • 使用绝对路径访问您的文件(避免像../foo.php这样的相对路径)
    • 两个_once函数都需要检查文件是否也通过符号链接包含,因为符号链接可能会产生到同一文件的多个路径。这是极其昂贵的。(见下一点)
  • 仅加载必要的文件比调用include要便宜得多。利用自动装载程序,在需要时才加载类。
  • 本地磁盘几乎总是比网络存储更好。如果有多个服务器,请尽量在每个服务器上保留源代码的副本。这意味着您需要在发布期间更新多个位置,但在性能方面值得努力。

总体而言,它取决于您的硬盘速度。但是与不加载文件或从RAM加载文件相比,文件加载非常缓慢。

希望这有所帮助。


谢谢提供的信息!非常有帮助!我使用自动加载器,这是我在CodeIgniter中改变的第一件事情之一。我猜你关于include的建议同样适用于require吧? - Naatan
@Naatan 是的,这两者之间唯一真正的区别就是它们在失败时抛出的错误/警告不同。 - Andrew Curioso
是的,我明白了,但是在 PHP 中你永远不知道 :) 再次感谢您的建议。 - Naatan

4

这是相当多的文件,但如果使用框架(比如Zend),这并不出乎意料。包含这么多文件的影响主要取决于服务器硬盘速度。无论如何,文件访问非常慢,因此如果可以的话,请减少包含文件的数量。

APC可以缓存所有这些文件的操作码到内存中,这意味着在缓存失效或销毁之前不需要再进行磁盘查找。

  • 尝试关闭APC并查看差异。执行时间应该明显增加。

  • 尝试使用xdebug对脚本进行分析。您很可能会发现,与文件访问相关的其他问题(代码问题)会更大程度地影响性能。


谢谢您的回复。我正在使用一个经过大量修改的 CodeIgniter 版本。我会采纳您的建议,使用 xdebug 进行分析以获取一些有用的信息。关闭 APC 对我没有太大帮助,因为我不知道时间差异是由文件加载还是由 APC 执行的其他优化造成的。 - Naatan

-1

我知道这是一个非常老的问题,但我也在想同样的事情,谷歌把我带到了这里。我最终在一个小型通用数字海洋服务器上使用 PHP 7.4(现已停止更新)进行了一些基准测试,该服务器具有 SSD 驱动器。

方法:

我创建了 260 个类,每个类都在自己的文件中。文件的命名方式类似于 A0.php、A1.php、A2.php、...、Z8.php、Z9.php。它们的大小为 608 字节,因此在阅读结果时请记住这一点。

我还创建了一个单独的文件 LotsOfClasses.php,其中包含所有 260 个类。该文件的大小约为 155k。

脚本是通过 PHP 命令行运行的,没有缓存。应该假设磁盘缓存按照底层 CentOS 操作系统的正常工作方式工作。

以下是测试脚本的基本外观(选择 microtime 而不是 hrtime 是因为它更容易被人类阅读):

<?php
$start = microtime(true);
foreach (range('A', 'Z') as $letter) {
    foreach (range(0, 9) as $i) {
        include "{$letter}{$i}.php";
    }
}
echo (microtime(true) - $start) . "\n";

并且

<?php
$start = microtime(true);
include "LotsOfClasses.php";
echo (microtime(true) - $start) . "\n";

我随后将每个脚本批量运行100次,共3次:

for i in {1..100}; do php benchmark.php; done > time.txt

我还编写了一个脚本来计算时间的平均值。虽然没有什么花里胡哨的东西,但为了完整起见,在这里也包括它:

<?php
$file = file_get_contents("time.txt");
$values = array_filter(explode("\n", $file));
echo (array_sum($values) / sizeof($values)) . "\n";

命令行结果:

包含260个类的1个文件的平均时间为11.79毫秒。

每个文件都有一个类,包含260个文件的平均时间为21.42毫秒。

Web服务器结果:

敏锐的读者可能会问,包含大约155k的单个文件需要11.79ms。这是否意味着如果您的应用程序加载100个这样的文件,仅包含文件就需要超过1秒钟?如果使用命令行,则答案是肯定的。但是,PHP通常通过服务器运行。在命令行上运行时,PHP必须每次解析文件。通过服务器加载文件时,PHP将使用其Opcache,速度显着更快。

包含260个类的1个文件的平均时间为0.056毫秒。

从Web服务器中包含260个文件的平均时间为0.65毫秒。

结论:

当通过 Web 服务器加载一个大文件时,几乎是瞬间完成的,但是在加载一个文件和 260 个较小文件时,只有 0.6 毫秒的差异。这种增加可以忽略不计,合并文件基本上只是微观优化。即使在更大的应用程序中,我也无法想象它会超过几毫秒的差异。此外,单个文件可能更复杂,难以维护。我认为,在加载多个可维护文件方面做出微不足道的时间增加的权衡是值得的。

值得一提的是,我还尝试了在 Web 服务器上使用 260 个文件版本的 include_once,它只增加了 0.08 毫秒的时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接