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

7

32

I understand that you can use either a PSR standard to locate files, or tell composer a directory to scan for classes. The documentation recommends using the PSR-4 standard. There is also an option for composer to create an optimized autoloader, which basically generates a full classmap. So why should one use PSR-4 at all if the best way to load is with a classmap?

It makes sense to me to keep the directory structure, since that is a good way to organize anyway. However, it seems like the logical option would be to use PSR-4 loading on development machines, and then classmap for the production environment. That way, you don't have to rebuild your classmap every time you create a new class, but the production environment creates a complete one as a part of the deployment process without an additional call to

./composer.phar dump-autoload -o
Bade answered 2/4, 2014 at 6:42 Comment(7)
All of the documentation recommends using PSR-4; the question is why not use classmap for all production instances?Bade
Can you provide a link to the documentation?Bemock
Thanks Timo--good idea. I added it inline.Bade
To be fair, if you use any kind of opcache (and now one actually comes with PHP) it doesn't matter at allAmphibolous
@Dracony- that makes sense, although you could take into account the number of nodes and frequency of deployment to arguably make a difference thereBade
@Amphibolous an opcache doesn't mitigate any stat calls like file_exists that an autoloader might do before loading such class file. A classmap removes the stat calls.Jetton
I think this question confuses the file mapping aspect (PSR-*) and autoload optimizations aspect (classmap) which are in theory completely separate issues.Distressful
T
35

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

Because it's more practical.

In production, you can use a classmap (with composer dumpautoload -o) because you won't add any new class, but in dev environment it's interesting to have the flexibility provided by PSR-0 or PSR-4 (i.e. nothing to do when adding new classes).

Update: you can also use composer install -o, it's simpler.

Tyratyrannical answered 2/4, 2014 at 9:33 Comment(10)
It seems like a lot of extra work; why don't they recommend using classmap in the "autoload" section, and psr-4 in the "dev-autoload" section? That would save extra commands/error exposure on deploys, without giving anything up.Bade
What? Extra work? Where? Locally, you run composer install, in production you run composer install -o. 2 more characters to type...Tyratyrannical
Per the documentation: This is recommended especially for production, but can take a bit of time to run so it is currently not done by default.Tyratyrannical
So you don't have to run composer twice--just once with the extra switch? That would make sense.Bade
Yes just once with -o. You can also use -o locally if you want to.Tyratyrannical
If you added both to the autoload section--would it check the classmap first, and then fallback to PSR4?Bade
I have no idea and I guess that's not really supported. But I don't get why you would want to do that.Tyratyrannical
"What? Extra work? Where?" I don't know about you, but if my directories and namespaces aren't PSR0/4 compliant, then I have to run composer dump-autoload -o. (maybe I'm doing something wrong though). It's a pain in the ass. Development should never be more of a pain in the ass than it has to be.Manzanilla
@Manzanilla I've not used something not PSR-0/4 for years. You should consider doing the same, then there is no pain at all.Tyratyrannical
And don't forget about --no-dev: Disables autoload-dev rules. for production releases.Gerdagerdeen
I
50

The problem is that the classmap is NOT actually faster in every case!

The speed of the classmap comes from not having to check the filesystem if a file exists before doing the always necessary work of loading it, parsing it (opcode caches will help here) and then executing it.

But the downside of the classmap is that you possibly generate a huge amount of data for every single class, interface and trait included in the libraries you use, without you actually using it in your production code. Loading huge arrays does not come for free - while the code need not be parsed again and again (opcode cache), it still has to be executed, the array data structure has to be put into memory, filled with lots of strings, and then eats up some amount of memory that might have been usable for something else.

I found two resources discussing this topic: First of all there is github issue #1529 suggesting further improvements for the composer autoloader using a bunch of symlinks to avoid having to scan multiple directories.

The discussion there also reveals that you should actually try to use the best possible namespace- or classname-prefix in the PSR-0 autoload declaration, i.e. the longest one possible. You can also use more than one prefix in the declaration.

Then there is a blog post linked in that issue that documents some xhprof benchmarks using a stock EZPublish 5 and fiddling with the settings, including APC Caching and classmap dumping.

Money quote:

This command created a 662KiB vendor/composer/autoload_classmap.php file containing an array that is a hash composed of the class name as index and the path to the file containing the class definition as value. At the time I am writing this post, this array is composed of 4168 entries. [...] Although it should give us the most efficiant autoloading mechanism, it actually slows things down (from 254.53 reqs/second to 197.95). The reason being that even if the file is cached by APC, the PHP array containing the map with more than 4100 entries needs to be re-created at every single request.

Will a classmap be fast? Certainly. Fastest in every case? Of course not - it depends on the ratio used vs. unused classes per request. So even if on average your application actually uses ALL classes in the map, a classmap might still be slower if you only use about 10% of the classes per request, and you'd be better off optimizing the autoload declarations of the libraries you use. In fact, every classname prefix should only ever point to exactly one directory.

Note that the performance gain you'd achieve only is in the area of about low single digit milliseconds per request. Your application surely is awesome if that figure is a significant performance boost in the range of 5 to 10%. But if you really are in that performance range, blindly believing that a classmap is ALWAYS faster probably wastes a lot of unnecessary CPU cycles.

If you optimize something: Measure it! How would you know if it actually becomes better if you cannot measure it?

Inconsolable answered 2/4, 2014 at 22:3 Comment(4)
+1 if you're going to configure your production deploys to dump classmaps, benchmark the real performance beforehand. In my case it was less performant.Betts
The speed of the autoload strategy and the file mapping are two independent issues. You can control the generation (and thus speed) of your autoload using optimization parameters which are independent of the file mapping. See my answer below.Distressful
@Distressful Your answer basically states that you can enable classmapped autoloading on demand. That is already part of the question you are answering. The question is if one should enable classmap autoloading all the time, or even opt for directly defining a classmap for autoloading, and the answer is: No.Inconsolable
@Inconsolable I don't think so. The question title is: "Why use a PSR-0 or PSR-4 autoload in composer if classmap is actually faster?" The body contains: "So why should one use PSR-4 at all if the best way to load is with a classmap?" I concluded that the OP did not understand the difference between file mapping and autoload optimisation. I might be wrong.Distressful
T
35

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

Because it's more practical.

In production, you can use a classmap (with composer dumpautoload -o) because you won't add any new class, but in dev environment it's interesting to have the flexibility provided by PSR-0 or PSR-4 (i.e. nothing to do when adding new classes).

Update: you can also use composer install -o, it's simpler.

Tyratyrannical answered 2/4, 2014 at 9:33 Comment(10)
It seems like a lot of extra work; why don't they recommend using classmap in the "autoload" section, and psr-4 in the "dev-autoload" section? That would save extra commands/error exposure on deploys, without giving anything up.Bade
What? Extra work? Where? Locally, you run composer install, in production you run composer install -o. 2 more characters to type...Tyratyrannical
Per the documentation: This is recommended especially for production, but can take a bit of time to run so it is currently not done by default.Tyratyrannical
So you don't have to run composer twice--just once with the extra switch? That would make sense.Bade
Yes just once with -o. You can also use -o locally if you want to.Tyratyrannical
If you added both to the autoload section--would it check the classmap first, and then fallback to PSR4?Bade
I have no idea and I guess that's not really supported. But I don't get why you would want to do that.Tyratyrannical
"What? Extra work? Where?" I don't know about you, but if my directories and namespaces aren't PSR0/4 compliant, then I have to run composer dump-autoload -o. (maybe I'm doing something wrong though). It's a pain in the ass. Development should never be more of a pain in the ass than it has to be.Manzanilla
@Manzanilla I've not used something not PSR-0/4 for years. You should consider doing the same, then there is no pain at all.Tyratyrannical
And don't forget about --no-dev: Disables autoload-dev rules. for production releases.Gerdagerdeen
R
12

here's what you'd need to do, if you added/changed classes:

  • classmap: composer dumpautoload (perhaps also update composer.json with a new classmap entry)
  • psr-0: nothing
  • psr-4: nothing

so basically you can go wild with psr-4 and psr-0 without having to worry, whether your newly created class is correctly in the autoloader. plus with it you get a free proper directory structure of your library, that represents your namespace.

autoloader files:

  • classmap: vendor/composer/autoload_classmap.php
  • psr-0: vendor/composer/autoload_namespaces.php
  • psr-4: vendor/composer/autoload_psr4.php
Revivalism answered 2/4, 2014 at 7:7 Comment(7)
Are you saying that PSR-4 automatically generates a classmap? And what do you mean by "free proper directory structure?"Bade
it doesn't generate a classmap, since psr-4 entries are autoloaded differently. in vendor/composer/autoload_psr4.php is only the root directory defined which points to a namespace. with free proper directory structure i mean when you put a class into lib/Controllers then that class also has x\y\Controllers in their namespace (assuming lib points to namespace x\y). whereas with psr-0 and classmap it could also have a complete different namespace.Revivalism
So classmap is faster than PSR-0, but not faster than PSR-4? Because there are no extra file_exists() calls?Bade
PSR-0 offers the same behavior in this regard as PSR-0, it just offers some more flexibility with the base path und drops the _ as fake namespace separator.Radarman
@BryanAgee The classmap should always be faster (maybe not with millions of classes, but than you've got other problems), it's just not very convenient when developing.Radarman
Now what about this: If you run composer install -o, even PSR-4 will be scanned for classes and create a classmap. This contradicts what @zwacky about PSR-4. And also the statement about PSR-0 is wrong: You only have to constantly dump a new autoloader if you use classmaps. PSR-0 and PSR-4 will automatically find new classes.Inconsolable
The class mapping and the autoload implementations are separate issues. Using the optimization options, you can generate a classmap for psr-0 and psr-4 mappings. See my answer below.Distressful
W
3

An important argument here is that using psr-4 or psr-0 in the composer.json forces you to organize your class files following a strict standard. This allows others (or yourself 2 years from now) who look at the composer.json to immediately know where your classes are.

If you do this wrong, e.g. if you misspell a namespace, then you will likely find out during development or in your unit tests due to a "class not found". This is good, because it forces you to fix this.

The classmap is much more forgiving, and will allow any arbitrary organization of class files, leaving the reader in the dark.

So, as others already said: Use psr-4 or psr-0 in the composer.json, work with that during development, and then consider the -o option for production. But measure if this really brings a performance benefit!

Woolfolk answered 20/9, 2014 at 14:4 Comment(0)
D
2

In summary, there are two concepts:

  1. Establishing the file mapping to let composer know which files to autoload.
  2. Generating the optimal autoload for your use case (development vs production).

You specify the file mapping using the PSR-0 or PSR-4 syntax. From the docs:

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

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

You specify the autoload implementation you want using one of the following options (for Level 1 optmizations) :

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

There are other optimization options (see the docs)

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

The optimization options will determine what type of autoload will be generated. No optimization will generate a trivial file lookup autoload which is ideal for development purposes. Other optimizations will use a classmap which is often faster for production use but needs to be rebuild every time a class is created, renamed or moved.

The confusion probably stems from the fact that the autoload configuration also accepts a classmap format:

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

But the doc clearly says:

You can use the classmap generation support to define autoloading for all libraries that do not follow PSR-0/4.

The keyword is "libraries", which are typically slow changing.

If your code does not map to PSR-0/4. Create your own autoloader for that.

You can transition to PSR-4 by using the autoload feature of composer to generate an autoloader for your new code and registering that autoloader and your own.

Distressful answered 3/2, 2023 at 14:52 Comment(0)
F
1

The problem with PSR-0 and PSR-4 (and the class-map) its implementation doesn't consider the optimization. The implementation is lacking at best.

However, the idea behind the class-map works.

I created a library that works generating a class-map. However, this class-map is way simpler yet it's optimized.

https://github.com/eftec/autoloadone

The map is reduced even for a big project, it groups the same classes of the same namespace if they are contained in the same folder. If not, then it's not a problem they are also included. Also, if the class lack of namespace, two classes in a file, the file is outside of the scope, it's not a problem, it tracks all of them. You could even exclude some folders or namespace.

For example, in a big project

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)

So, for a project with 898 classes, the map uses only 12.9kb.

And what's the difference in performance:

  • it doesn't need to scan a folder (for example if the file doesn't exist).
  • it doesn't verify if the file doesn't exist.
  • it's only a single file. So, the overhead is a single include, not 3 for

Composer's autoload includes (for each call) the next files:

  • autoload.php
  • composer/ClassLoader.php (it depends in the configuration)
  • composer/autoload_real.php
  • composer/autoload_namespaces.php
  • composer/autoload_psr4.php
  • composer/autoload_classmap.php (89kb)

or it runs the files:

  • autoload.php
  • composer/ClassLoader.php (it depends in the configuration)
  • composer/autoload_static.php (107kb)

While Opcache does marvel but we are still including, at least, two includes (instead of one), plus one of them is huge and it is still an overhead, and it is still per call.

So, which is faster. It depends on the project but I checked that usually PSR-0 is faster. However, the difference is dim, both are slow. :-P

Flotsam answered 31/10, 2018 at 0:15 Comment(0)
L
0

The question is misleading.

"classmap" as the autoloading option is more accurately just a dumb directory glob with a reference to every file it comes across that has a class with a matching name. It then compiles all of that into the "classmap array", which ALSO has PSR-0 rules in there.

So, PSR-0 and classmap use the same classmap, meaning there is literally no difference.

You use PSR-0 because you want to autoload PSR-0 code.

Lilylivered answered 3/4, 2014 at 8:12 Comment(4)
Isn't this only true when using the -o option to optimize the autoloader?Bade
This is wrong. The classmap goes into vendor/composer/autoload_classmap.php, while the PSR-0 goes into vendor/composer/autoload_namespace.php. And the PSR-4 goes into vendor/composer/autoload_psr4.php. PSR-0 does NOT create a classmap of any kind.Inconsolable
Bryan: Yes, of course, which there is no reason to not do if you're concerned about speed as the OP is.Lilylivered
Sven: classmap and PSR-0 go into vendor/composer/autoload_classmap.php if you're using the -o tag as suggested. You skip -o locally and use it on production, meaning you get flexible locally and fast on production. Win, right? The only difference here is that so far -o is not supported for PSR-4 which is a shame, and is being worked on by a few people. Still, its a trivial speed difference so it is not to be concerned about.Lilylivered

© 2022 - 2024 — McMap. All rights reserved.