Because of a preloading bug in PHP 7.4.12 I deactivated preloading in one of my applications, which gave me the chance to compare the speed between different opcache settings:
- Preload almost all classes/functions used on most pages and turn opcache.validate_timestamps on, so every used PHP file which is not preloaded is checked for updates on every request
- No preloading and opcache.validate_timestamps turned off, so opcache never needs to check if a file changed
- No preloading and opcache.validate_timestamps turned on, so every used PHP file is checked for updates on every request
The second option was the way I ran my application for a long time, but I used some generated and often changing PHP files for caches, so I had to invalidate those regularly with a script, which was cumbersome.
Some background about the application:
- An online shop built on the Symfony framework
- Code base started 11 years ago
- About 700 PHP files used for each page
- 25’000 to 30’000 pages per day by users (without bots)
- Uses MariaDB as a database and Elasticsearch for commonly used data, like products and orders, which makes most pages quite fast
- Runs on bare metal servers from 2014 with SSDs and a lot of RAM
- PHP runs in docker images from thecodingmachine
- When preloading is turned on, about 2300 files are preloaded and use about 34mb of memory, while opcache uses about 73mb of memory in total (according to opcache_get_status)
The following measurements start at PHP execution time:
|Settings||Average all pages||Average simple page||Bootstrapping time|
|Preloading + validate.timestamps=1||~25 ms per page||~18 ms per page||~3.3ms|
|validate.timestamps=0||~27 ms per page||~20 ms per page||~7.7ms|
|validate.timestamps=1||~38 ms per page||~27 ms per page||~15ms|
The bootstrapping time measures the time until a controller is reached within the application, this is basically the dependency injection and Symfony bootstrapping cost as an average across 1000 requests, with no usage of databases, purely PHP execution time.
As you can see, preloading is not much faster than turning off validate.timestamps, as checking all the PHP files for changes is what takes the most time. Preloading does cache all the PHP files when PHP-FPM starts (before processing requests), so the application always starts with most files already cached in opcache, which is a big benefit on its own.
Performance costs of using a framework almost disappear with preloading, as it only takes 3.3ms to bootstrap the application. It will be interesting to see if PHP8 can reduce this even more and what impact JIT will have. I did try preloading + validate.timestamps turned off, and bootstrapping is still at 3.3ms - the few files PHP has to check do not have a measurable impact.