Last week I did some benchmarks with PHP 7.3 and 7.4 using Symfony 4.4 with a hacked-together version of eZ Platform. The benchmark results showed significant performance gains, but I could not get the test app to work with hallmark feature of PHP 7.4: OPCache Preloading. I had everything in place, I thought I'd give it another go with a simpler app.
I was curious to verify the estimates given in a recent blog post on Symfony.com:
During preloading, PHP also resolves class dependencies and links with parent, interfaces and traits. It also removes unnecessary includes and performs some other optimizations. The overall result is a performance improvement of 30%-50% in real applications.
- New in Symfony 4.4: Preloading Symfony Applications in PHP 7.4
Because of the trouble with dependencies earlier, I started with the bare minimum. I installed a fresh copy of Symfony 5.0.1 and and added a controller that returned a bare-bones response with "Hello world". The infrastructure and testing methodology were identical to the previous round, but this this time I did benchmarks with PHP 7.4 with and without OPCache Preloading enabled. Without further ado, here are the results:
Again, it's not worth mulling over the exact throughput of the demo application, but focus on relative numbers. We see some consistent improvements when enabling OPCache Preloading on 7.4, but not alone warranting the 30-50% increase. However the cumulative improvements over PHP 7.3 are realistically well within the reach of the made claims.
Also, please note that the benchmark is not what you would call a "real world application". It's just a hello world, where the preloaded bootstrap is minimal (for a Symfony app). I expect OPCache Preloading to yield bigger gains the larger your application grows.
PHP extensions can break OPCache Preloading
Unfortunately I ran into some trouble again and could not get any benchmarks executed with OPCache Preloading enabled. This time the trouble was caused by the php7.4-sqlite3 package that the app required because of it's use of an SQLite database. I tweaked some settings like the opcache.interned_strings_buffer, that had caused similar errors before.
I could not get it to work and eventually had to give up. In my use case, when enabling the SQLite extension and restarting PHP-FPM, the system persistently logged fatal errors like:
[08-Dec-2019 11:18:54] WARNING: [pool www] child 1097 exited on signal 6 (SIGABRT - core dumped)
2019/12/08 11:16:41 [error] 3538#3538: *153 recv() failed (104: Connection reset by peer) while reading response header from upstream,
In my experience adopting OPCache Preloading in PHP 7.4 is not straightforward today. In addition to userland dependencies causing havoc, it seems that extensions like SQLite can also cause unexpected failures. I'm sure this'll change over time as issues get ironed out.
So if your app has trouble working with OPCache Preloading, I suggest you start dropping out dependencies from your composer.json as well as disabling PHP extensions one by one. Once you've found the cause of the it'll be easier to fix (if you've got the skills) or at least to create a minimal test case and report the bug to PHP, extension or PHP library project.
Update 1: I got a response on this post from Nikita Popov from the PHP dev team. His initial reaction was that SQLite or PHP extensions in general are unlikely the cause here. Instead it is probably because of require instead of opcache_compile_file(). A fix is being worked on in Symfony, but it the require behavour could be fixed in PHP 7.4.1.
Update 2: I tried again after PHP 7.4.1 was released problem persisted and I discovered that using MySQL extension also crashes the system. But again I got a response from Nikita, stating that this is expected. Some fixes did not make it to that version, and more OPCache Preloading fixes are coming in PHP 7.4.2 and the reason they for the crashes are not database extensions, but the incompatibilities with Doctrine - a database library (and ORM):