Introduction

WordPress is clearly one of the world’s most used Content Management Systems (CMS) commanding over 35% of the internet, and over 60% of the CMS market [1].

Working in Operations (ops), you see your fair share of friends, family and businesses that ask you to set up a WordPress site as their go to blogging platform. This really comes down to WordPress’ ease of use, massive library of themes and it’s modular nature that enables the extension of it’s core via the use of plugins.

After hearing one of my clients wanting to move forward with another WordPress site, I thought it was a good time to investigate PHP 7.2/.3/.4, clean up my docker-compose stack and experiment with different caching technologies in order to make it load as fast as possible for end-users.

Science! 🧪

Initially my WordPress deployment comprised of MySQL, php-fpm (PHP FastCGI Process Manager), and nginx with no caching optimizations. In order to work out if the optimizations were actually doing anything, I needed to gain a baseline benchmark using my current stack. Luckily, since everything was composed via docker, this was very easy to do.

Method

In order to keep the variables under control the following hardware and software was used across all benchmark tests:

Hardware:

  • Mac Book Pro 2011
    • CPU: Intel® Core™ i7-2720QM CPU @ 2.20GHz × 8
    • RAM: 3.8GiB
    • OS: Ubuntu 18.04.4 64-bit

Software:

  • WordPress version 5.3.2
  • NGINX version 1.16.1
  • MariaDB version 10.4.12
  • Docker 19.03.8
  • Docker Compose v1.25.4
  • Hey v0.1.3 [2].

~ Shameless Self Promotion ~

  • stackme [3] - to provide a base docker-compose stack to get WordPress up and running.

PHP Configurations:

  • php-fpm 7.2.28
  • php-fpm 7.2.28 + opcache
  • php-fpm 7.2.28 + opcache + varnish cache
  • php-fpm 7.2.28 + opcache + w3 total cache plugin
  • php-fpm 7.2.28 + opcache + w3 total cache plugin + varnish cache
  • php-fpm 7.3.15
  • php-fpm 7.3.15 + opcache
  • php-fpm 7.3.15 + opcache + varnish cache
  • php-fpm 7.3.15 + opcache + w3 total cache plugin
  • php-fpm 7.3.15 + opcache + w3 total cache plugin + varnish cache
  • php-fpm 7.4.3
  • php-fpm 7.4.3 + opcache
  • php-fpm 7.4.3 + opcache + varnish cache
  • php-fpm 7.4.3 + opcache + w3 total cache plugin
  • php-fpm 7.4.3 + opcache + w3 total cache plugin + varnish cache

I decided to opt for hey as the choice of benchmark tool, as it’s similar to the apache benchmark tool (ab), but more modern, capable of using multiple cpus.

Our default benchmark will be run using the following configuration:

1
hey -z 60s -o csv http://localhost:80

This configures hey to flood the WordPress server for 60 seconds with requests, with a default of 50 worker threads.

Hypothesis

  • PHP by itself will be the slowest
  • PHP with OpCache should provide minor performance boosts
  • PHP with w3TotalCache should be pretty performant, but will be limited by PHP.
  • PHP with Varnish should be the fastest, as it doesn’t have to hit PHP after initial page render.

Results

One thing I was surprised about was how much of a difference OpCache made in terms of processor usage and the impact that had to response time. I guess it shows how much overhead the interpreter can cause. (check out the screenshots under extras for nerds)

Having used other languages such as Python [4], and Java [5] I was somewhat surprised that PHP’s OpCache wasn’t added until 2012. That’s only 8 years ago…

The other compounding issue with OpCache is that, while it has been bundled with PHP since version 5.5 [6], it may not be enabled by default depending on where you get the binary from, or if your php.ini file hasn’t set opcache.enable=1. So make sure to enable this if you can!

Lastly, my hypothesis about Varnish turned out to be correct, but I did find that having W3TotalCache enabled as well added extra overhead, so you probably don’t need to use both.

fig 1. PHP Response Times and PHP With OpCache Enabled, without caching.
fig 2. PHP With OpCache Enabled, with different caching strategies
fig 3. Requests per Second, with different caching strategies

Conclusion/TL;DR

  • Where possible, always run PHP with OpCache enabled.
  • If you can’t run varnish, enable w3TotalCache - it’s pretty damn performant.
  • For best performance, run with varnish.

Last Thoughts and Disclaimers

This benchmark runs with the best performance you can get out of WordPress. It’s a fresh installation with no other plugins enabled. This enabled testing of the underlying technologies - therefore mileage may vary depending on how many plugins you have enabled on your WordPress site.

I found to get the best out of W3TotalCache a lot of settings had to be tweaked to get it to work for anything other than the main/front page. So do experiment to ensure you get the best you can out of it.

In this benchmark, W3TotalCache was configured to make use of PHP’s Alternative PHP Cache (APC) for object caching. APC provides an in memory caching API that developers can use.

Certain default Caching technologies may not work well for sites that make use of backend processing on the fly. That said, if your WordPress site uses JavaScript to update the loaded page and API calls, you should be safe. In any case it’s best to run a test site introducing your caching technology before fully rolling it out.

Extras for Nerds

‘Sup Nerds. Check out some extra graphs around system stats and overall performance with max response times.

Benchmark repository including benchmark scripts, the raw data and database.

Moar Response Time Graphs

fig 4. Performance across all variants
fig 5. PHP With OpCache Enabled, with different caching strategies, including Maximum Response time

System Stats

I grabbed screenshots for the first round of benchmarking to see what the impact each stack/configuration would have on CPU and RAM usage. RAM Usage was fairly similar, but CPU Usage differed a lot.

fig 6. Base System Metrics
fig 7. PHP 7.2 Metrics
fig 8. PHP 7.2 OpCache Enabled Metrics
fig 9. PHP 7.2 OpCache Enabled Metrics, With W3TotalCache
fig 10. PHP 7.2 OpCache Enabled Metrics, With Varnish
fig 11. PHP 7.2 OpCache Enabled Metrics, With W3TotalCache and Varnish

Random

I found it interesting that W3TotalCache injected an HTML comment, which could be useful while debugging performance metrics for those of you heading down the W3TotalCache route.

fig 12. W3TotalCache adds caching information in an HTML comment

Addendum

  • Time to Blog: 16h:15m