Some time ago I received an email from a client experiencing slow performance issues with a LEMP (Linux, Nginx, MySQL and PHP web server). During a full audit, I found that the server’s load average was pretty low (see above screenshot). However, the website was indeed very slow. There were some misconfigurations, but one of the main PHP performance bottlenecks was due to poor CPU single thread performance.
Upon further investigation, I found that the volume of traffic was not enough to activate all 18 cores concurrently. Also, each CPU core, took anywhere from 1 to 3 seconds on average to complete PHP requests. As mentioned, there were other code efficiency issues, but for the purpose of this article, we will focus on: CPU single thread performance or the speed of your CPU cores.
First, before we get to that, let’s look at how PHP uses your web server’s CPU. As you may have already noticed, PHP is not designed for multithreading. Therefore, each page/request is served by one PHP process and each process locks on to one CPU core. This is also the case when PHP waits for MySQL queries to complete. However, unlike PHP, MySQL is multithreaded, but that’s another topic.
Basically, this is the way PHP was designed to function. Which means, if your web server has more than one page request at a time, you’ll have several PHP processes/CPU cores running concurrently. This makes your choice of CPU very important!
With 18 cores on this low-traffic server the server’s load average always remained below 18. In fact, during monitoring, no more than 6 – 10 cores were being used by PHP concurrently during peak traffic. As a result, in addition to other recommendations included in my PDF audit report, the following was also recommended:
You have a lot of CPU cores (18) but the core speed is only 2.0GHz. Since PHP processes are executed per-core, a VPS with 3+GHz cores would fit your workload better.
Faster CPU cores vs Additional CPU cores
As mentioned, the server had a total of 18 CPU cores, with a below-average core speed of 2.0GHz. It was clear from monitoring, that although the PHP application and MySQL queries needed optimization, the issue was made even worse by the slow CPU single thread performance. Let’s look at the importance of having a good blend of speed and capacity …or faster CPU cores vs. additional CPU cores.
Traffic was relatively low for this server with approximately 6 to 10 cores being used during peak traffic.
If it takes a 2GHz processor core 3 seconds to process a request, then the same request would be returned in around 2 seconds by a 3GHz processor core. Which in turn, frees up cores for additional requests at a faster rate. This means we can safely reduce the # of cores from 18 to 8. Although the concurrent capacity would drop a bit, the server’s max throughput would actually increase by over 30%. The result being improved scaling and a faster end-user experience! Of course, if this server was instead experiencing high load averages (18.00+), with say 3+GHz cores already installed, then the recommendation would be different. In this case and in many cases, a lot of performance can be gained by also addressing PHP and other related performance misconfigurations. For this server with all the recommendations and optimizations combined, it resulted in requests taking 100ms to 300ms on average, instead of 1 – 3 seconds.
The client was not to blame, because when they informed their web host about slow web application performance, the host, instead of alerting them to the of the lack of CPU single thread performance, they instead suggested to upgrade the VPS package which featured additional CPU cores. However, during each of these upgrades, the 2.0GHz clock speed of the CPU cores remained unchanged, which provided exactly 0% improvement to server throughput!
Checking CPU core speed and number of cores – Linux command line
If you are unsure of your server’s CPU specs. You can quickly check with the following command:
This will list your CPU specs:
root@vps01 [~]# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 8 On-line CPU(s) list: 0-7 Thread(s) per core: 1 Core(s) per socket: 8 Socket(s): 1 NUMA node(s): 1 Vendor ID: GenuineIntel CPU family: 6 Model: 62 Model name: Intel(R) Xeon(R) CPU E5-1650 v2 @ 3.50GHz Stepping: 4 CPU MHz: 3499.998 BogoMIPS: 6999.99 Hypervisor vendor: KVM Virtualization type: full L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 12288K NUMA node0 CPU(s): 0-7
Use nproc to display only the # of cores or cat /proc/cpuinfo for all cores listed with specs.
For CLI-only PHP scripts, read here. For the next blog post, I hope to write about one of these topics: hardware selecting/sizing, application bottlenecks or MySQL optimization. Please subscribe to updates using the email form below.
Published: May 6, 2017 | Last Updated: April 30th 2019