Optimizing Composer's autoloader performance
Lately, we spent quite some time optimizing Mouf's performance. It appeared that one of the bottlenecks was Composer's autoload mechanism. Our application was spending quite some time in the autoloader instead of doing interesting things.
I was curious to see how much was spent in Composer's autoloader, so I did a little benchmark with a simple "Hello World!" application in Mouf.
A bit of theory first
Mouf comes with a great number of Composer packages. For my test, I started with the typical 67 packages a Mouf application has (yeah, that's a lot).
Each package relies on PSR-0 autoloading, and in each composer.json
of each package, I have this:
"autoload": {
"psr-0": {
"Mouf": "src/"
}
}
So basically, each package informs Composer that any class starting with the Mouf
namespace might be part of that package.
So when Composer encounters a Mouf class (for instance Mouf\Utils\Common\ConditionInterface\AndCondition
, it does not know in which package to search for it. It has to run through the 67 packages
where it might be. How does Composer search for a class? By performing calls to the file_exists
function, in each package. So when the autoloader is triggered for a class in the Mouf
namespace, it can do up to 67 calls to file_exists
(!).
When you know that file_exists
is particularly slow on Windows platform, you start to understand why your application could be faster.
Hopefully, Composer comes with the possibility to dump an optimized autoloader:
php composer.phar dumpautoload -o
This creates a "classmap" mapping each class to its file in a big array. When you use an optimized autoloader, you don't perform any calls to file_exists
so your application is faster.
Benchmarking the optimization
How faster is an application with an optimized autoloader?
To find this, I wrote a typical "Hello World" application with Mouf and Splash (Mouf's MVC framework). The "Hello World" text is displayed inside a Bootstrap template. Benchmarking is performed using Apache's ab
tool, with one concurrent thread and 300 measures.
The test is performed in an Ubuntu 13.04 machine, with a Core I7.
Results:
Basic autoloader | 18.7 ms |
---|---|
Optimized autoloader (php composer.phar dumpautoload -o ) | 11.8 ms (37% faster!) |
Conclusion: By optimizing the autoloader, we can have increase performance by 37% (!). On a Windows machine, that would be even higher.
The drawback
The biggest drawback is that the optimization of the autoloader must be performed by the application developer. The application developer might not know that
it has to perform the optimization. Furthermore, each time the application developer will perform a php composer.phar update
, the autoloader will be reseted
to the non-optimized one. And each time a new class is added, the autoloader must be dumped again so the class is added to the classmap generated by
the autoloader.
So basically, the responsibility of doing the optimization lies in the application developer hands. As a framework developer, I would like to do as much as possible to avoid that, and as much as possible to have a responsive framework out-of-the-box, even with a non optimized autoloader.
The solution: more specific namespaces
By looking at the way Zend Framework is writing its composer.json
files, I realized that you can be very specific on the namespace. You don't have to restrict yourself to the
vendor name. You can go further. For instance:
"autoload": {
"psr-0": {
"Mouf\\Utils\\Common\\": "src/"
}
}
or
"autoload": {
"psr-0": {
"Mouf\\Utils\\Logger\\": "src/"
}
}
This is a very interesting feature, because if all packages are restricted to the the smallest namespace possible, the number of calls to file_exists
will be really smaller.
So I decided to optimize my 67 composer.json
files by selecting the more specific namespace I could.
The results are below:
Results:
Basic autoloader | 18.7 ms |
Non optimized autoloader, but with specific namespaces | 14.6 ms (22% faster) |
---|---|
Optimized autoloader (php composer.phar dumpautoload -o ) | 11.8 ms (37% faster) |
Conclusion
The autoloader's performance is really important in modern PHP applications. Optimizing Composer's autoloader using php composer.phar dumpautoload -o
is the way to have maximum performance. But it is the responsibility of the application developer to apply the optimization.
The framework developer can still help the application developer by providing very specific namespaces in each of its composer.json
autoload section and by ensuring that each
namespace is tied to only one package. This optimization is really worthwhile (22% increased performance in Mouf's case!)
Tags :
- david's blog
- Log in to post comments
What’s new on Mouf :
- TDBM 5.0 is released 09/11/2017
- Quickstarting a Mouf appl... 15/06/2017
- Building beautiful datagr... 05/10/2016
- Official release of TDBM... 14/06/2016
- Announcing Mouf 2.1 first... 01/03/2016
- Mouf + Magento + PSR-7 =... 30/06/2015
- Standardizing the way we... 10/06/2015
- Container interoperabilit... 09/06/2015
- Container-interop + PSR7... 01/06/2015
- New in Mouf: a console! 19/05/2015