如果类映射实际上更快,为什么要在作曲器中使用 PSR-0 或 PSR-4 自动加载


Why use a PSR-0 or PSR-4 autoload in composer if classmap is actually faster?

我知道您可以使用PSR标准来查找文件,或者告诉作曲家一个目录来扫描类。文档建议使用 PSR-4 标准。作曲家还有一个选项来创建优化的自动加载器,它基本上会生成一个完整的类映射。那么,如果最好的加载方法是使用类映射,为什么要使用 PSR-4呢?

保持目录结构对我来说是有意义的,因为无论如何这都是一个很好的组织方式。但是,似乎合乎逻辑的选择是在开发计算机上使用 PSR-4 加载,然后对生产环境使用 classmap。这样,您不必每次创建新类时都重新构建类映射,但生产环境会在部署过程中创建一个完整的类映射,而无需额外调用

./composer.phar dump-autoload -o

问题是类映射实际上并不是在所有情况下都更快!

类映射的速度来自于在执行加载文件、解析文件(操作码缓存在这里会有所帮助)然后执行它之前不必检查文件系统是否存在文件。

但是类图的缺点是,你可能会为你使用的库中包含的每个类、接口和特征生成大量的数据,而没有在生产代码中实际使用它。加载巨大的数组不是免费的 - 虽然代码不需要一次又一次地解析(操作码缓存),但它仍然必须执行,数组数据结构必须放入内存中,填充大量字符串,然后消耗一些可能可用于其他东西的内存量。

我找到了两个讨论这个主题的资源:首先,github问题#1529建议使用一堆符号链接进一步改进作曲家自动加载器,以避免扫描多个目录。

那里的讨论还表明,您实际上应该尝试在 PSR-0 自动加载声明中使用最好的命名空间或类名前缀,即尽可能长的命名空间前缀。还可以在声明中使用多个前缀。

然后有一篇博客文章链接到该期,该文章使用股票 EZPublish 5 记录了一些 xhprof 基准测试,并摆弄设置,包括 APC 缓存和类映射转储。

货币报价:

此命令创建了一个 662KiB 供应商/作曲家/autoload_classmap.php 文件,其中包含一个数组,该数组是由作为索引的类名和包含类定义作为值的文件的路径组成的哈希。在我写这篇文章的时候,这个数组由 4168 个条目组成。 [...] 虽然它应该为我们提供最有效的自动加载机制,但它实际上减慢了速度(从 254.53 reqs/秒到 197.95)。原因是即使文件由 APC 缓存,每个请求都需要重新创建包含超过 4100 个条目的映射的 PHP 数组。

类映射会很快吗?当然。在每种情况下都最快?当然不是 - 这取决于每个请求使用的类与未使用的类的比率。因此,即使平均而言,您的应用程序实际上使用了映射中的所有类,如果您每个请求仅使用大约 10% 的类,类映射可能仍然会更慢,并且您最好优化您使用的库的自动加载声明。事实上,每个类名前缀应该只指向一个目录。

请注意,您只能实现每个请求的低个位数毫秒数。如果这个数字在 5% 到 10% 的范围内显着提高性能,那么您的应用程序肯定很棒。但是,如果您真的处于该性能范围内,盲目地认为类映射总是更快可能会浪费大量不必要的 CPU 周期。

如果你优化了什么:衡量它!如果你无法测量它,你怎么知道它是否真的变得更好?

如果类映射实际上更快,为什么要在作曲器中使用 PSR-0 或 PSR-4 自动加载?

因为它更实用。

在生产环境中,您可以使用类映射(带composer dumpautoload -o),因为您不会添加任何新类,但在开发环境中,拥有 PSR-0 或 PSR-4 提供的灵活性很有趣(即添加新类时无需执行任何操作)。

更新:你也可以使用composer install -o,更简单。

如果您添加/更改了类,则需要执行以下操作:

  • classmap:composer dumpautoload(也许还会用新的classmap条目更新composer.json)
  • PSR-0:
  • PSR-4:

所以基本上你可以疯狂地使用PSR-4和PSR-0,而不必担心你新创建的类是否正确在自动加载器中。 此外,您还可以获得库的免费正确目录结构,该结构代表您的命名空间。

自动加载器文件:

  • 类映射:供应商/作曲家/autoload_classmap.php
  • PSR-0:供应商/作曲家/autoload_namespaces.php
  • PSR-4:供应商/作曲家/autoload_psr4.php

这里的一个重要论点是,在 composer.json 中使用 psr-4 或 psr-0 会强制您按照严格的标准组织类文件。这允许查看 composer.json 的其他人(或 2 年后的您自己)立即知道您的课程在哪里。

如果你做错了,例如,如果你拼错了一个命名空间,那么你可能会在开发过程中或在单元测试中发现,因为"找不到类"。这很好,因为它迫使您解决此问题。

映射更加宽容,并且允许任意组织类文件,使读者处于黑暗之中。

所以,正如其他人已经说过的:在 composer.json 中使用 psr-4 或 psr-0,在开发过程中使用它,然后考虑使用 -o 选项进行生产。但是,请衡量这是否真的带来了性能优势!

总之,有两个概念:

  1. 建立文件映射,让作曲家知道要自动加载哪些文件。
  2. 为您的用例(开发与生产)生成最佳自动加载。

您可以使用 PSR-0 或 PSR-4 语法指定文件映射。从文档中:

{
    "autoload": {
        "psr-4": {
            "Monolog''": "src/",
            "Vendor''Namespace''": ""
        }
    }
}

参考: https://getcomposer.org/doc/04-schema.md#autoload

您可以使用以下选项之一指定所需的自动加载实现(用于级别 1 优化):

Set "optimize-autoloader": true inside the config key of composer.json
Call install or update with -o / --optimize-autoloader
Call dump-autoload with -o / --optimize

还有其他优化选项(请参阅文档)

参考: https://getcomposer.org/doc/articles/autoloader-optimization.md

优化选项将确定将生成哪种类型的自动加载。任何优化都不会生成一个微不足道的文件查找自动加载,这是开发目的的理想选择。其他优化将使用类映射,该类映射对于生产用途通常更快,但每次创建、重命名或移动类时都需要重新构建。

这种混淆可能源于自动加载配置也接受类映射格式的事实:

"autoload": {
        "classmap": ["src/addons/*/lib/", "3rd-party/*", "Something.php"]
    }

但文档明确指出:

您可以使用类图生成支持来定义不遵循 PSR-0/4 的所有库的自动加载。

关键字是"库",通常变化缓慢。

如果您的代码未映射到 PSR-0/4。为此创建您自己的自动加载器。

您可以使用作曲家的自动加载功能为您的新代码生成自动加载器并注册该自动加载器和您自己的自动加载器,从而过渡到 PSR-4。

PSR-0 和 PSR-4(以及类映射)的问题 它的实现不考虑优化。实施充其量是缺乏的。

但是,类映射背后的想法是有效的。

我创建了一个可以生成类映射的库。但是,这个类映射更简单,但它经过优化。

https://github.com/eftec/autoloadone

即使对于大型项目,映射也会减少,如果它们包含在同一个文件夹中,它会对同一命名空间的相同类进行分组。 如果没有,那么它们也包括在内不是问题。另外,如果类缺少命名空间,一个文件中有两个类,该文件在范围之外,这不是问题,它会跟踪所有这些类。 您甚至可以排除某些文件夹或命名空间。

例如,在一个大项目中

Number of Classes: 898
Number of Namespaces: 62
Number of Maps: 117
Number of PHP Files: 1693
Number of PHP Autorun: 0
Number of conflict: 1
Ratio map: 6.91% (less is better. 100% means one map/one file)
Map size: 12.9 kbytes (less is better, it's an estimate of the memory used by the map)

因此,对于具有 898 个类的项目,地图仅使用 12.9kb。

性能有什么区别:

  • 它不需要扫描文件夹(例如,如果文件不存在)。
  • 它不会验证文件是否不存在。
  • 它只是一个文件。因此,开销是单个包含,而不是 3 个

作曲家的自动加载包括(对于每个调用)下一个文件:

  • 自动加载.php
  • 作曲家/类加载器.php(这取决于配置)
  • 作曲家/autoload_real.php
  • 作曲家/autoload_namespaces.php
  • 作曲家/autoload_psr4.php
  • 作曲家/autoload_classmap.php (89kb)

或者它运行文件:

  • 自动加载.php
  • 作曲家/类加载器.php(这取决于配置)
  • 作曲家/autoload_static.php (107kb)

虽然 Opcache 确实很棒,但我们仍然至少包括两个包含(而不是一个),而且其中一个是巨大的,它仍然是一个开销,它仍然是每个调用。

所以,哪个更快。这取决于项目,但我检查了通常PSR-0更快。然而,差异是模糊的,两者都很慢。 :-P

这个问题具有误导性。

"classmap"作为自动加载选项更准确地说只是一个愚蠢的目录 glob,它引用了它遇到的每个文件,这些文件都有一个具有匹配名称的类。然后,它将所有这些编译到"classmap数组"中,该数组中也有PSR-0规则。

因此,PSR-0 和类映射使用相同的类映射,这意味着实际上没有区别。

使用 PSR-0

是因为要自动加载 PSR-0 代码。