How to Benchmark PHP Performance on Your Webdock Server

Last updated: August 29th 2024

Assessing the performance of your PHP application is crucial for ensuring it operates efficiently on your web server. This process involves measuring execution times and resource consumption to identify bottlenecks, optimize code, and make informed decisions regarding server configurations.

You can gain insightful perspectives into your application's performance with the appropriate tools and techniques. One highly effective tool for this purpose is the PHP Benchmark Script developed by Sergio Brighenti.

This article will walk you through the steps of utilizing this script to benchmark your PHP application, including how to incorporate MySQL benchmarks for a thorough analysis.

Why Benchmark?

Before exploring the specifics of the PHP Benchmark Script, it’s essential to grasp what benchmarking entails and its significance. In software development, benchmarking means measuring the performance of a system or application. This can include evaluating various aspects, such as execution time, memory usage, and response times under different loads.

Benchmarking aims to detect performance bottlenecks and understand how well an application performs under various conditions. It enables developers to make data-driven decisions regarding optimizations and enhancements.

These are some of the reasons you should benchmark your servers:

  1. Identify Bottlenecks: By measuring execution times for different segments of your application, you can identify where slowdowns occur. This information is vital for optimizing code and enhancing overall performance.
     
  2. Compare Performance: Benchmarking allows you to compare the performance of different PHP versions or server configurations. This is especially beneficial when upgrading or migrating applications.
     
  3. Resource Utilization: Understanding how your application uses server resources aids in optimizing server configurations for improved performance and cost-effectiveness.
     
  4. User Experience: Faster applications contribute to better user experiences. Optimizing performance through benchmarking can increase customer satisfaction and retention.
     
  5. Performance Regression Testing: Regular benchmarking helps ensure that new code changes do not adversely affect performance, allowing you to maintain high standards over time.

What is PHP Benchmark Script?

While there are plenty of benchmarking scripts in the market, we like the PHP Benchmark Script because it is a straightforward, no-nonsense, yet powerful tool for comparing the raw performance of different PHP versions and server setups.

You can find the script on GitHub. It enables you to measure execution times for various control flows and functions, offering insight into how well your application operates under different conditions. Here are some features of the script that stand out:

  1. Execution Time Measurement: The script calculates how long various operations take, enabling you to identify sluggish parts of your application.
     
  2. Custom Benchmarking: You can create custom benchmarks tailored to your specific requirements, providing flexibility in testing.
     
  3. Multiple Tests: The script supports additional tests, including file I/O and MySQL performance, facilitating comprehensive analysis.

Getting Started with the PHP Benchmark Script

To begin with, download the PHP Benchmark Script to your server. You can either run it directly from SSH:

$ wget https://raw.githubusercontent.com/SergiX44/php-benchmark-script/master/bench.php

You can also download bench.php from GitHub and upload it to your server’s document root using an FTP client or SSH. Once the bench.php script is placed on your server, run the command:

$ php bench.php

The results may be something like:

-------------------------------------------------------

|       PHP BENCHMARK SCRIPT v.2.0 by @SergiX44       |

-------------------------------------------------------

PHP............................................. 8.3.10

Platform......................................... Linux

Arch............................................ x86_64

Server...................................... mysqlbench

Max memory usage.................................. 512M

OPCache status................................ disabled

OPCache JIT....................... disabled/unavailable

PCRE JIT....................................... enabled

XDebug extension.............................. disabled

Difficulty multiplier............................... 1x

Started at..................... 27/08/2024 20:28:55.483

-------------------------------------------------------

math.......................................... 0.2478 s

loops......................................... 0.1978 s

ifelse........................................ 0.3773 s

switch........................................ 0.3393 s

string........................................ 0.5775 s

array......................................... 1.0332 s

regex......................................... 0.3602 s

is_{type}..................................... 0.3528 s

hash.......................................... 0.1736 s

json.......................................... 0.3661 s

-------------------------------------------------------

Total time.................................... 4.0255 s

Peak memory usage................................ 2 MiB

Let’s take a quick look at what each pointer means:

  • PHP Version: Indicates the version of PHP running on the server (e.g., 8.3.10). It affects performance and available features.
     
  • Platform: The operating system on which PHP is running (e.g., Linux). This can influence compatibility and performance.
     
  • Architecture: This refers to the system architecture (e.g., x86_64), which indicates whether it’s 32-bit or 64-bit, which can affect memory handling and performance.
     
  • Server: The name of the server (e.g., mysqlbench) which is just a label and does not affect benchmarking directly.
     
  • Max Memory Usage: PHP's maximum amount of memory (e.g., 512M). This is important for resource-intensive scripts.
     
  • OPCache Status: Indicates whether the OPcache (a PHP caching engine) is enabled or disabled. Enabling it can improve performance by caching compiled script bytecode.
     
  • OPCache JIT: Shows if Just-In-Time compilation is available for OPcache. This can further enhance performance.
     
  • PCRE JIT: Indicates if the PCRE (Perl Compatible Regular Expressions) Just-In-Time compilation is enabled, which can speed up regex operations.
     
  • XDebug Extension: Indicates if the XDebug debugging tool is enabled. While it provides useful debugging features, it can slow down performance.
     
  • Difficulty Multiplier: A factor indicating how the benchmark's performance is scaled; typically set to 1x for standard testing.
     
  • Started At: The timestamp when the benchmark started, useful for tracking and logging purposes.
     
  • Math, Loops, IfElse, Switch, String, Array, Regex, Is_{type}, Hash, JSON: These are individual benchmarks that measure the time taken to perform specific operations in PHP. Each entry shows the time in seconds it took to complete that particular task.
     
  • Total Time: The overall time taken to run all the benchmark tests combined (e.g., 4.0255 s).
     
  • Peak Memory Usage: The maximum amount of memory used during the benchmark test (e.g., 2 MiB), indicating how resource-intensive the operations were.

The benchmark script allows you to change the difficulty multiplier to adjust the intensity of tests. You can do this by passing the --multiplier parameter as follows:

$ php bench.php --multiplier=2

This parameter increases the number of iterations for each test, which can be beneficial for stress-testing your server.

When you're running the PHP Benchmark Script, it's important to recognize the differences in how OPcache behaves in the command line interface (CLI) versus a web server environment. Typically, OPcache isn't enabled by default in the CLI, which can give a false impression of performance if you're only testing there. To make use of OPcache in CLI, you'll need to turn it on explicitly in your php.ini file.

On the flip side, Webdock stacks come with OPcache already activated for PHP-FPM. This setup is designed to boost performance when running PHP scripts through a web server. So, if you run the bench.php script in a browser, you can see performance improvements ranging from 30% to 40% compared to running it via CLI.

This speed boost comes from OPcache's ability to cache compiled script bytecode, which reduces the time needed to run PHP scripts by reducing the overhead of parsing and compiling code with each request.

It's best to run the benchmark script through a web server instead of in the CLI for accurate benchmarking that reflects how your application performs under typical usage. PHP Benchmark Script lets you test your server even on a browser. Let’s take a look at how you can do so:

First, you’d need to log in as root:

$ sudo su

And open your website’s root folder:

# cd /var/www/html

Then, download the script to this location:

# wget https://raw.githubusercontent.com/SergiX44/php-benchmark-script/master/bench.php

If you have already downloaded it to the /home/(user) folder, then you can also move the file to the website folder:

# mv /home/(user)/bench.php /var/www/html

Remember to replace (user) with the username of your admin username.

Once you have the file in place, simply visit your website domain followed by /bench.php. This would load for a few seconds before showing you results, so don’t think the action is being unresponsive.

If you want to customize the benchmarking process (like changing the difficulty multiplier), you can modify the script or add parameters to the URL. For example:

https://yourdomain.com/bench.php?multiplier=2

This method gives you a clearer picture of how your application will behave in real-world situations and helps you pinpoint any bottlenecks and opportunities for optimization.

Using OPcache effectively in a web environment can provide valuable insights into your PHP application's performance, enabling you to make informed decisions about optimizations and improvements. When benchmarking, always keep in mind the environment you're working in to ensure your results reflect actual usage conditions.

You can add further benchmarks to the PHP Benchmark Script to obtain more extensive performance metrics. These include benchmarks for:

Random Number Generation:

$ wget https://raw.githubusercontent.com/SergiX44/php-benchmark-script/master/rand.bench.php

The rand.bench.php script measures the time it takes to generate a specified number of random numbers. This can help evaluate the performance of the server's random number generation capabilities, which is useful for applications that rely on randomness, such as gaming or cryptography.

File I/O Operations:

$ wget https://raw.githubusercontent.com/SergiX44/php-benchmark-script/master/io.bench.php

The io.bench.php script performs various file operations, such as creating a file, writing data to it, reading the data back, and deleting the file. This gives insights into the server's file system performance, which is crucial for applications that handle large amounts of data or require frequent file access.

MySQL Performance:

$ wget https://raw.githubusercontent.com/SergiX44/php-benchmark-script/master/mysql.bench.php

The mysql.bench.php script typically runs a series of queries to test how quickly the database can handle them, including insertions, updates, deletions, and selections. This helps identify potential bottlenecks in database performance and can inform optimization strategies for database-driven applications.

Once you download the additional scripts, simply run the bench.php command. Moreover, you can create your own tests by placing a file in the same directory as bench.php. Name the file with the pattern *.bench.php.

This file should return either a closure or an array of closures. Each closure will accept a parameter called $multiplier, which determines the difficulty of the test. A higher $multiplier means a longer test duration, with the default set to 1. It’s important to select a reasonable number of iterations for your test, such as 1000, and then multiply that by the $multiplier you choose.

Running the MySQL Benchmark Script

Before I get into action, there are a few prerequisites you need to know. Firstly, I am running the test on Webdock’s famous LEMP server stack. Also, for benchmarking, your MySQL user should be the ‘admin’ to prevent conflicts. You can find these details easily on Webdock’s dashboard:

image1.png
Now that’s out of the way, let’s try running the MySQL performance script. You'll need to provide the above-mentioned database credentials to execute MySQL benchmarks using the mysql.bench.php script. The typical command would be:

$ php bench.php --mysql_user=admin --mysql_password=yourpassword --mysql_host=localhost

Replace yourpassword with the actual password for the admin user. This command will carry out various database operations (such as select, insert, update, delete) and measure their performance.

Once you run the MySQL benchmark, you may obtain output similar to this after the regular PHP benchmark results:

-----------------Additional Benchmarks-----------------

mysql::ping................................... 0.0001 s

mysql::select_version......................... 0.0336 s

mysql::select_all............................. 0.1028 s

mysql::select_cursor.......................... 0.2919 s

mysql::seq_insert............................. 0.8913 s

mysql::bulk_insert............................ 0.4699 s

mysql::update................................. 1.1086 s

mysql::update_with_index...................... 0.2139 s

mysql::transaction_insert..................... 1.0088 s

mysql::aes_encrypt............................ 0.0400 s

mysql::aes_decrypt............................ 0.0389 s

mysql::indexes................................ 0.1555 s

mysql::delete................................. 0.9489 s

-------------------------Extra-------------------------

mysql::select_version::q/s....................... 29805

mysql::select_all::q/s............................ 9730

mysql::seq_insert::q/s............................ 1122

mysql::update::q/s................................. 902

mysql::update_with_index::q/s.................... 14232

mysql::transaction_insert::t/s..................... 991

mysql::aes_encrypt::q/s.......................... 25253

mysql::aes_decrypt::q/s.......................... 25866

mysql::delete::q/s................................ 1054

-------------------------------------------------------

Total time.................................... 9.1651 s

Peak memory usage............................... 10 MiB

Let’s take a quick look at what each pointer means:

  • mysql::ping: Time taken to check the connection to the MySQL server. A quick way to see if the server is responsive.
     
  • mysql::select_version: Time taken to retrieve the version of the MySQL server.
     
  • mysql::select_all: Time taken to select all rows from a table.
     
  • mysql::select_cursor: Time taken to select rows using a cursor, which allows processing results one row at a time.
     
  • mysql::seq_insert: Time taken to perform sequential inserts into a table, one row at a time.
     
  • mysql::bulk_insert: Time taken for inserting multiple rows in a single query, which is generally faster than sequential inserts.
     
  • mysql::update: Time taken to update records in a table.
     
  • mysql::update_with_index: Time taken to update records in a table that has an index, which can affect performance.
     
  • mysql::transaction_insert: Time taken to insert records within a transaction, ensuring ACID properties.
     
  • mysql::aes_encrypt: Time taken to encrypt data using AES encryption.
     
  • mysql::aes_decrypt: Time taken to decrypt data using AES encryption.
     
  • mysql::indexes: Time taken to access or process indexes on the tables.
     
  • mysql::delete: Time taken to delete records from a table.
     
  • mysql::select_version::q/s: This measures the number of queries per second (q/s) required to select the version of the MySQL server. A higher value indicates better performance in retrieving server information.
     
  • mysql::select_all::q/s: This indicates the queries per second for selecting all records from a database table. It reflects the efficiency of reading data from the database.
     
  • mysql::seq_insert::q/s: This shows the number of sequential insert operations per second. It evaluates how quickly the database can handle inserting new records.
     
  • mysql::update::q/s: This represents the queries per second for updating existing records in the database. A higher number suggests better performance in modifying data.
     
  • mysql::delete::q/s: This measures the queries per second for deleting records from a database table. It indicates how efficiently the database can remove data.

Understanding Filesystem Settings' Impact on Performance

When benchmarking database performance, it's vital to consider how your server's filesystem settings influence results. For instance, Webdock’s infrastructure has a setting for direct I/O writes called sync=standard; all direct writes are flushed to disk immediately. This setting ensures data safety but can significantly affect performance.

With sync=standard

Let’s take a look at the implications of using sync=standard versus a more lenient setting. Here’s an example of how these settings might influence MySQL performance from a recent MySQL benchmark we took of a server:

-------------------------Extra-------------------------

mysql::select_version::q/s........................... 30431
mysql::select_all::q/s............................... 13763
mysql::seq_insert::q/s............................... 262
mysql::update::q/s.................................. 274
mysql::delete::q/s.................................. 248

-------------------------------------------------------

Total time............................................ 18.3358 s
Peak memory usage.................................... 12 MiB

Without sync=standard

-------------------------Extra-------------------------

mysql::select_version::q/s........................... 28583
mysql::select_all::q/s............................... 7849
mysql::seq_insert::q/s............................... 3894
mysql::update::q/s.................................. 3472
mysql::delete::q/s.................................. 4215

-------------------------------------------------------

Total time............................................ 3.9725 s
Peak memory usage.................................... 12 MiB

As illustrated above, disabling direct I/O synchronization can substantially improve insert speeds and overall query performance.
But why does Webdock enable sync=standard by default? There are several reasons, including, but not limited to:

  • Data Integrity: By enabling sync=standard, you ensure that all data written to the disk is properly flushed and stored before any confirmation is sent back to the application. This reduces the risk of data loss in case of a system crash or power failure, as only committed transactions will be lost, rather than potentially corrupt or incomplete data.
     
  • Improved Performance: While it may seem counterintuitive, using sync=standard can enhance performance for certain workloads. By batching write operations, the server can optimize how data is written, reducing the number of individual disk writes. This can lead to more efficient use of disk I/O resources, especially under heavy load.
     
  • Consistency Across Restarts: With sync=standard enabled, you can achieve higher consistency when your server restarts. The data that was acknowledged as written before a restart will be intact, allowing for a more reliable recovery process. This is particularly important for applications that require a high level of consistency, such as databases or transaction-based systems.
     
  • Easier Troubleshooting: With sync=standard, you can simplify troubleshooting and diagnostics. Since data is written in a consistent manner, it becomes easier to trace issues related to data integrity and application behavior. If problems arise, you can be more confident that the data you are examining is complete and accurately reflects the state of your application at the time of the write operation.

If your application is write-heavy — indicating it performs numerous database insertions — you may want to contact Webdock support to discuss disabling direct I/O synchronization with the underlying filesystem.

Note: While this change can potentially enhance performance (often by 4-5 times), it comes with associated risks of data loss in case your server crashes or if there’s a power outage in the data center. To mitigate this, you may want to have a robust backup strategy ready if you implement this change.

Tips Before Taking Action

Before making any alterations to your server to improve performance based on benchmarking results, I would recommend conducting thorough testing in a controlled yet similar environment. This enables you to evaluate how changes might impact both performance and stability without affecting production systems.

In Conclusion

Benchmarking is an essential component of maintaining and optimizing your PHP applications. Using the PHP Benchmark Script provides valuable insights into your application's performance under varying conditions.

Adding additional benchmarks for file I/O and MySQL operations gives you a holistic view of your application’s performance.

Moreover, understanding how filesystem settings like sync=standard affect database operations is crucial for making informed decisions regarding server configurations. If you manage a write-heavy application, consider contacting Webdock support to explore options for enhancing database insert performance.

Incorporating these practices will help ensure that your PHP applications operate efficiently on your web server, providing an enhanced user experience and maximizing resource utilization. Happy Benchmarking!

Meet Aayush, a WordPress website designer with almost a decade of experience who crafts visually appealing websites and has a knack for writing engaging technology blogs. In his spare time, he enjoys illuminating the minds around him.

Related articles