To Make PHP-FPM Faster, Put it to Sleep: The Power of the Sleep() Function

by Loucas Cavacopoulos, full stack developer

In the web world, speed is always a top priority, especially when dealing with high-traffic applications based on PHP-FPM (FastCGI Process Manager) and Nginx. It may sound strange, but sometimes, in order to make our application run “faster,” we need to make it… sleep!

Yes, I’m referring to PHP’s sleep() function. But before we talk about how sleep() makes a script run faster, we need to understand how PHP-FPM works.

PHP-FPM is designed to handle a large number of requests by maintaining a “worker pool.” Each “worker” handles one request (e.g., opening a page) and performs all the calculations and tasks it requires. With the parallel execution of the same script by its workers, PHP-FPM can serve many requests simultaneously. Combined with a web server like nginx, it can serve hundreds (or thousands) of users simultaneously.

It’s important to remember that each “worker” runs independently, without any knowledge of its other “parallel” siblings, and executes the same PHP script sequentially. Up to this point, there is no programmatic “parallelism” or asynchronous execution. However, it’s quite easy for workers to make them update a “queue” (e.g. running in Redis or ElasticSearch) orchestrated by another worker or a third script – so, we have the same script work in parallel iterations, achieving parallelism in a very simple and straightforward way. The possible scenarios are endless.

However, when multiple PHP workers run simultaneously, they compete for system resources like CPU and memory, and worse, some of their requests depend on very “slow” systems (like APIs or databases). This leads to moments when the system becomes slow, results in timeouts or finally collapses.

The solution here resembles what we would do when driving on the highway!

The impatient driver pushes the gas pedal harder, but along with other impatient drivers, they quickly cause a traffic jam. In our case, this means adding more resources, CPU cores and caching mechanisms, requiring higher priority and lower latency for our tasks… but the result remains the same: congestion.

In the other hand, using the simple command sleep(), we just… stop for a while, for a few microseconds, we pull our script to the side, until the congestion of various workers, threads, and calls dissipates, allowing everyone to pass by. And so, we often reach our destination much faster, because we helped clear the road!

This is where the sleep() function, PHP’s “non-blocking” function, comes into play.

The Purpose of the Sleep() Function

<?php 
sleep(2); // wait 2 seconds before continuing
usleep(100); // wait 100 microseconds before continuing
?>

The sleep() function (and its faster sibling, usleep()) simply pause the execution of a script for a given number of seconds (or microseconds in the case of usleep()). This might seem like something insignificant, but it proves incredibly useful and beneficial in a lot of scenarios.

In Linux and in some versions of Windows (but not all), when running PHP-FPM, sleep() does not “block” execution at the system level.

This means that when the PHP script calls sleep(), the operating system puts the worker handling that particular iteration into a sleep state and fully releases the CPU. (Note that the script remains protected and retains its full state, i.e., all data remains in tact and continue without any changes once the sleep duration is over.)

During the sleep period, even if that’s a small portion of a second, the worker does not consume processing power, and PHP-FPM can allocate resources to other workers, allowing them to handle new incoming requests.

Since sleep() does not block the entire PHP-FPM process, but only the specific thread, this ensures that even when some tasks are paused or delayed, PHP-FPM continues to serve other requests efficiently. This improves synchronization and resource management, enabling the parallel handling of multiple requests without unnecessary bottlenecks caused by other types of waiting functions.

Thus, you achieve the following:

Rate Limiting: If an external API imposes a rate limit, using sleep() allows you to wait a specific time between API calls.
Efficient Waiting: Instead of looping while waiting for a resource or lock to become available, sleep() can be used to pause the script.
Reducing Load on Other Services: In some cases, delaying the execution of certain operations reduces the load on other services and allows for more efficient background processing.

And, last but not least: Using sleep() can actually speed up, yes, speed up our script!

Of course, overusing it or placing it in inappropriate parts of the script can lead to reduced overall performance. It’s important to analyze where pauses are genuinely necessary and use them to achieve good “synchronization” in the execution of your program with all the other “threads” running concurrently in your application’s environment.

Nevertheless, the next time you’re optimizing your PHP-FPM configuration, remember that sometimes the best way to speed things up is to let your code rest by taking a …nap.

Comments are closed, but trackbacks and pingbacks are open.