When I chose Symfony, I did so on the understanding that it was going to be a relatively lightweight, performance framework which I could use to build web apps which scale nicely and respond quickly. I started out with a tutorial where the first activity was to get the name of a person input via the GET request in the URL, and display it on screen. Simple enough - can be developed incredibly quickly and be a non-issue for performance. Not so much...
If you don't want to see what I went through, you can stop here. The problem wasn't with Symfony - it was with Vagrant. Don't use Vagrant on an non-SSD (I've not tried on an SSD) for development.
That being said, here's the problem, and what I tried...
As shown above, it was taking 14 seconds to get the name from the URL and display it on the page due to everything Symfony was doing on the setup. 14 seconds to load what amounts to the following code:
echo "Hello " . $_GET['name'];
The plain PHP returned (according to Chrome's developer tools) in 322ms, with 19.11ms on the server. 97.7% faster. Something's not right - time to take a closer look.
Clocking on the time element of the Symfony bar at the bottom of the screen took me through to the performance metrics section where the full request is broken down.
Ok, lots of information, but largely useless. Assuming that's the process it needs to go through, there needs to be a way to improve those areas. Maybe a configuration issue? Luckily, there's a section for that, too!
Yes, my Vagrant box for this development is running Ubuntu 18.04, and I have PHP 7.2 installed. Great. I know I have Xdebug installed, it's in my Vagrantfile. So what's APCu, and what does it do?
Turns out it's a Cache Adapter which can "significantly increase an application's performance". Great! Sounds exactly what I need. Off to the Vagrant box to get it installed using:
sudo apt-get install -y php-apcu && sudo service apache2 restart
This will also try to install php-apcu and php-apcu-bc, then it will restart Apache for us (because I develop on the LAMP stack at the moment - LEMP to come at a later date). Refresh the page...and....14 seconds still! I suddenly remembered that following any major change so far, I've had to run "composer update" on the Symfony root directory. Still not sure why, but this gives:
A reduction of 36%, with it coming down to 9.1 seconds. Memory usage did drop by 66%, so bonus there. Still, nothing to shout about for that so more digging to go. Off to Stack Overflow where there's an answer relating to Symfony2 which we might be able to use. First on the list is setting the realpath_cache_size in the php.ini to be 4M or greater. It also mentions updating the cache ttl to be 7200, so we'll do that whilst we're in that area. I set the cache size to be 8M, so it should cover the full size of the request we're running.
A quick restart of Apache following the change, and then the composer update rocketed the time to 15.7 seconds. Worse than before. Hopefully the second load would be better! Refresh...and...6.7seconds. Still not good enough - especially for a first load. Changing the name so it wasn't cached resulted in 6.5 seconds. Need better than that, even if we are down by over 54% of our original time. Stack Overflow suggests turning xdebug off. I know it's a bit of a hog at times, but let's give it a go. All at once, because I'm lazy:
sudo apt-get remove -y php-xdebug && sudo service apache2 restart && composer update
First load later, 15.5 seconds. Second load 6.9 seconds. Worse than before. Turning Xdebug back on resulted in faster page loads again, but still down at 6.5 seconds. At this stage I figured I'd increase the realpath cache size. I decided on a couple of different checks for this:
- Increase the cache size to 32M. It's a fairly high number, so should help. This managed to attain a first load time of 15.6 seconds, with a refresh time of 7 seconds. Not great. A change in the name passed into the URL resulted in the same load time.
- Increase the size to 128M. This was simply because the size of the vendor folder was hovering at 80MB, so we can try to cache all of that. First load time in this instance was 15 seconds, second load 7 seconds, and a name change load time of 6.5 seconds. Same as 8M so it's not worth changing.
Not a lot was working, so I looked to the Vagrant box I was using to see if there was anything there which could help. By default the box is set up to have 1GB RAM, so i increased this to 4GB to help. This, combined with setting the mount type of the file share to nfs (adding ', type: "nfs"' to the end of the sync folder line) brought the time down to a very regular 7 seconds on every load.
I started getting immensely frustrated with this. There was no way this could be a widely used framework and be so slow. Then I thought about having gone through Vagrant as a test machine. Maybe that's the issue. What if that's something which just gets accepted in development, but it's known to be faster in production. So I built a new virtual machine to run by itself (with sufficient resource to be a development machine if needed).
After setting up a new Ubuntu 18.04 VM with 8GB RAM and getting PhpStorm installed, I was all ready to go. I didn't want to simply run the code and be done, I wanted a fresh install of Symfony, so I recreated all of the code. Here's the debug bar for it:
A total of 97ms to do everything on the server. Ok, this is still around 5 times slower than the raw PHP version used earlier, but the library support built in and the various components which can be included suddenly make this a viable option. I'll likely have to ditch Vagrant for times when I'm using a framework though.