Some time ago, I received an email from a client experiencing slow performance issues with a LEMP (Linux, Nginx, MySQL, and PHP webserver). 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 poor CPU single-thread performance.
Upon further investigation, I found that web 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 we will focus on CPU single-thread performance (CPU core speeds) for this article.
Each PHP-FPM process uses a single CPU core. PHP benefits greatly from CPU single-thread performance.
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.
This is the way PHP was designed to function. If your web server has concurrent page requests, you’ll also have several PHP processes – each using a CPU core – 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 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 the right 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 a 3GHz processor core would return the same request in around 2 seconds. 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 slightly, the server’s max throughput would increase by over 30%. The result being improved scaling and a faster end-user experience! Of course, if this server were instead experiencing a high load average (18.00+), with say 3GHz+ cores, the recommendation would be different. In many cases, addressing PHP and other related performance misconfigurations will result in performance gains. With optimizations applied, this server took 100ms to 300ms on average, instead of 1 – 3 seconds.
Client’s old server w/ slow 2.0GHz cores
The client was not to blame because when they informed their web host about slow web application performance, the host, instead of alerting them of the lack of CPU single-thread performance, they instead suggested upgrading 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 precisely zero percent improvement to server throughput!
Checking CPU core speed and number of cores – Linux command line
Suppose 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: February 26th, 2021