This summer we worked together with David on the performance of a Magento 2 project and found some general improvements that the whole Magento community will benefit from.
Plugins querying the current Magento Version
Many plugins support multiple minor versions of Magento (2.0, 2.1, 2.2 or 2.3) and may need to check the version of core, to invoke slightly different behavior that changed over the different versions.
As a plugin developer, this is possible by calling
ProductMetadata::getVersion() inside your code or plugin. However, this
method is not just a "getter" like its name suggests, but it actually performs
a quite expensive operation.
See in this Tideways callgraph, that the call takes 174ms for the request of our customers shop:
The reason is here that the method actually uses the Composer API to query the
version of Magento, instead of returning a fixed constant. The Composer API
used loads all package versions, this can include packages that get their
version set by Git branch, which causes Composer to do a shell call to
retrieve the version.
Again naming strikes as a hard problem of software engineering here. Especially
for public APIs of frameworks that are used by different developers than the
authors, it is important to set expectations about the performance by
naming methods well. If the name would have been
extractVersionFromComposer(), then plugin developers would maybe have
realized the negative performance implications much earlier.
Now, thanks to David's Pull Request to Magento, the return value of this method will be cached indefinitely across requests since version 2.3.4, fixing the bottleneck after 4 years in a stable Magento releases.
If you still run lower minor versions of Magento, you can fix that problem by
getVersion() to return a harcoded value, for example
Implicitly Resetting Magento Compile Step
What we realized looking at the callgraphs was that a lot of code was using
development-environment factories, even though the container was built with the
setup:di:compile and should be in production environment.
This was caused by the command
module:enable -all executed after the compile
step and resetting the cache partially. In hindsight it makes sense that the
compile step generates the config cache for the current configuration, and when
you enable modules then the compile step needs to be re-executed.
As for the specific shop we worked on, response times dropped as much as 700ms
from 1 second to 300ms by caching the call to
getVersion() and fixing the DI cache.