Within last 2 weeks I’ve got a lot of different emails related to the “Symfony vs Flask vs Spring Boot – part 2 – performance benchmark” article. I am really impressed. To be honest I did not expect that current theme will raise so high interest. The most often question, which appeared at your emails, was coming from PHP+Symfony fans. It was related to the problem that base Symfoyn skeleton, the same as default PHP configuration options, are far from optimal. Yes, your are completely right. That is why that unplanned 3d part appeared. At current article I will try to perform Symfony and PHP tuning to maximize performance. So, let me tell you, step by step, what actions I performed.
First of all, as a lot of readers noticed, XDebug was installed and turned on. So, I removed it from docker image. At second, I enabled OPcache with next options:
[opcache] opcache.enable=1 opcache.validate_timestamps=0 opcache.memory_consumption=256 opcache.max_accelerated_files=20000
After that, I rerun tests with Apache benchmark utility. Here what I’ve got:
In case 100 request and 5 concurrency level, mean response time decreased from 50 to 33 milliseconds. In case ab -n 20000 -c 1000 I’ve got speed up from 4897 to 2441 milliseconds. Not bad. After that I continued the tuning and applied several more optimization, mostly using symfony performance optimization recommendations. As result, I performed next actions:
- Tried to compile entire container at single file using parameter .container.dumper.inline_factories: true – it gave me almost nothing
- Added OPcache class preloading by adding next lines to opcache configuration:
- Configured the PHP realpath cache using next configuration options:
; php.ini ; maximum memory allocated to store the results realpath_cache_size=4096K ; save the results for 10 minutes (600 seconds) realpath_cache_ttl=600
- Optimized composer autoloader with:
composer dump-autoload --no-dev --classmap-authoritative
After that, I rerun tests again and have got next results (red color – “After step 2”):
Yes, increase in performance was not so spectacular as within 1st step – but still 2441 milliseconds, in case max overload, were replaced with 2245 value – so I reached 2.2 sec break point. In case lower overloading the difference was less noticeable. Then I decided to try JIT compilation, that can be applied at PHP 8. According to documentation: “Introduced in PHP 8, JIT compilation has the potential to significantly boost your application’s speed and efficiency”. For those who are interested I found rather good explanation of how it works:
“Traditional PHP applications use an interpreter to execute code, which reads and processes the source code line by line during runtime. While this approach offers flexibility, it can also result in slower performance due to the overhead of processing time interpreting code on the fly. JIT compilation bridges the gap between interpretation and compilation. Instead of interpreting the code at runtime, JIT compilation dynamically compiles frequently executed portions of the code into native machine code. This compiled code can be directly executed by the CPU, bypassing the need for interpretation and significantly improving the execution speed. The key advantage of JIT compilation is its ability to optimize code execution based on real-time performance data. By identifying “hot” code paths that are executed frequently, the JIT compiler can focus its optimization efforts on these critical sections, resulting in substantial performance gains.”
So, I enabled according feature trying different combinations, the next one appeared to be optimal:
Here what I’ve got in 3d step of optimization (by using JIT) – green color at chart:
Generally, to be honest, I expected more. But still, in case max overloading (20000 users with 1000 concurrency level), I could reach 2sec. At my further attempt, I tried to adjust fpm options. Here is the list of parameters I was playing with:
pm = on-demand pm.max_children = 140 pm.start_servers = 36 pm.min_spare_servers = 36 pm.max_spare_servers = 108 pm.process_idle_timeout = 1m pm.max_requests = 0
I used PHP-FPM Process Calculator for that purpose. Unfortunately, I could not achieve any noticeable boost. In summary – I passed over 50 different articles trying to speed Symfony application. Using different framework and PHP optimizations, I was able to speed up mean response almost in 2.5 times, which really much more better then it was previous.
When I performed tests with siege -t1 -c200 -f urls.txt, results improved from 1.1 sec to ~0.5 sec. Yes, it is still not 0.18 sec as it was in Java Spring Boot case. Looking also at Apache Benchmark from part 2, where Spring Boot showed ~0.8 sec for overloading in 20000 users with 1000 concurrency level, suppose that we all can agree, that Java+Spring Boot is still faster in 2 times (and that is without any tuning). But we have remember from 1st part “Symfony vs Flask vs Spring Boot – part 1 – usability and speed of coding” that we have to pay for that performance a high price – much more slower coding and much more time consuming debugging. From my personal experience appears that coding Web API applications using Symfony is at least 2 times faster than in case using Spring Boot. Looking at charts, we see that Spring Boot is 2 times faster then Symfony in case high overloadings. My teacher from physics liked to mention law of energy conservation: “Energy does not arise from nothing and does not disappear into nowhere, but can only be transformed from one form to another…”. 🙂
Hope you liked current material. If you want to dive deeper at building search microservices with Spring Boot, Symfony or Flask – then welcome to my courses at udemy:
- Elasticsearch as you have never known it before (you may find a discount coupon here)
- Elasticsearch at AWS using terraform and ansible (you may find discount coupon here)
You also may subscribe to my newsletter. Thank you for your attention.