Author: Christian Vigh
Updated on: 2017-04-10
Posted on: 5 months ago
Categories: PHP Tutorials, PHP Performance, News
Read this article to learn how performance improved across the latest PHP versions starting from PHP 5 up to the latest developments, including the recent version 7.1 with opcache optimizations, as well as the experimental JIT branch that will be become part of PHP 8 or PHP 7.2 the next version.
This article will be improved over time with more information and more benchmark results to give a better idea of how PHP performance evolved over the years including newer versions that are not yet released. If you have corrections or suggestions for improvements, please comment below.
Since its original creation in 1994 by Rasmus Lerdorf, the PHP language has gone through radical evolution. While the first releases were simply external CGI programs developed mostly as a one-person project, Rasmus Lerdorf was joined by Andi Gutmans and Zeev Suraski for the radical redesign of the language's 3rd version as well as the creation of the PHP development group that has grown tremendously since then.
As the project grew and thanks to the extensible nature of PHP 3, PHP exploded in functionality, in both core and additional extensions which brought many additional functions in different domains such as network communications, parsing, caching and database support.
The language itself also evolved, bringing in a wide set of improvements. This includes support for object-oriented constructs, such as classes, interfaces, traits, closures, etc..
For many developers, adding new features, however, is not enough. As the language grew in popularity, there were more and more demands from the PHP community to provide better performance, scalability and less memory usage.
The PHP development team has for almost 2 decades devoted tremendous efforts to address these demands. While the introduction of PHP 3 improved performance substantially, it wasn't until the introduction of the Zend Engine by Andi Gutmans and Zeev Suraski and the release of PHP 4, that PHP's performance started getting serious.
The new in-memory compiler and executor model that was introduced back in 2000 dramatically improved the performance of PHP, often by a factor of 5 or even 10, and for the first time enabled the use of PHP for serious Web applications and sites. And we can say that, today, the results are well beyond what anybody could expect when the PHP project was born.
The booming popularity of PHP only increased the appetite for improved performance. Thankfully, the model introduced in the Zend Engine provided excellent grounds for continuous improvement.
And while PHP 5.0 did not bring substantial gains, and in some cases was even slower than 4, a team in Zend led by Dmitry Stogov and greatly helped by the community has been able to continuously optimize the language in subsequent versions, and managed to realize performance gains between 1.5x and 3x in most cases, by the time PHP 5.6 was released.
But a major breakthrough has been made with PHP 7.0 in December 2015. Version 7.1, announced in December, 1st 2016 also brought its own set of enhancements.
There is more to come with a really promising version currently under development managed by Dmitry Stogov of Zend. It is based on PHP 7.1 but it the actual version number it will be launched has not been defined. So for it is under the "experimental JIT" branch.
The key feature that has been introduced uses Just-In-Time (JIT) compilation, a technique to translate code into another format, such as native machine code of the underlying machine CPU, right before running it. JIT is supposed to make your programs run faster. We will see that it is not just a promise.
This article covers the results of a few benchmarks, from the very first release of PHP 5 up to the experimental JIT branch of PHP, currently under development (versions prior to PHP 5 are not included here).
At the time of this writing we don't know if there will be another major version before PHP 8, possibly PHP 7.2. But it is fair to assume that by the time PHP 8 is launched, it will already include the benefits of the currently experimental JIT branch.
For now this article only includes benchmark results of scripts running pure-CPU task, I mean tasks that do not require I/O operations, such as access to files, network or database connections.
The benchmark scripts that were used are listed below:
- bench.php available in the php-src/Zend folder of the PHP sources distribution ( )
- micro_bench.php also available in the php-src/Zend folder of the PHP sources distribution
The benchmark scripts were run using only the latest minor release of each PHP major version. So the versions that were tested are as follows.
- The experimental JIT branch currently under development
Of course, I wanted to be sure, so we ran all the intermediate versions on the same benchmarks like for instance between 5.3.0 to 5.3.29. The results were eloquent: the major enhancements in terms of performance have not been brought by intermediate releases, but by changes on major version numbers, like for example from PHP 5.4 to PHP 5.5, or from PHP 5.6 to PHP 7.
Intermediate releases did not show any noticeable performance improvements. This means that the same script should run more or less at the same speed, whether you are using PHP 5.4.0 or PHP 5.4.45.
You can have a look at the benchmark process section for a detailed explanation on how the host system was setup, how individual benchmarks were run, and how the timing results were interpreted.
The section gives the benchmark results per PHP version and benchmarks results.
Each benchmark column shows 3 values:
- Time: execution time, in seconds and milliseconds
- % rel. gain: Gain in execution time, relatively to the previous version. In the table below for example, the value of % rel. gain for bench.php and version 5.3.29 is 31.89%, meaning that the script ran 31.89% faster than with version 5.2.17.
- abs. gain: Indicates how much faster the script runs, when compared to PHP 5.0. If you have a look at the intersection of this column for bench.php and experimental JIT branch for example, you will notice that version 8 is more than 41 times faster than PHP 5.0 for this particular benchmark.
Results of the pure CPU benchmarks are shown below:
|PHP version||bench.php||micro_bench.php||mandelbrot.php (3)|
|time||% rel. gain||abs. gain||time||% rel. gain||abs. gain||time||% rel. gain||abs. gain|
|Experimental JIT branch||0.208s||54.68%||41.587||2.185s||35.41%||8.910||4.680s||49.28%||53.733|
(1) The benchmark could not be run on versions prior to 5.3, because it uses object features that were not yet implemented.
(2) The results in this column are a little bit biased, because the benchmark needs at least PHP 5.3 to run. Take them as purely informational, since they cannot be compared with PHP 5.0 performance.
(3) This is a modified version of the original mandelbrot.php script, which became too fast to be measured accurately using versions 7.1.0 and Experimental JIT branch. We simply ran the computations 100 times instead of 1 inside the script.
Of course, these were pure-CPU benchmarks. They don't cover all the aspects of PHP performance, and they may not be representative of real-word situations. However, the results are significant enough to justify a few comments:
- PHP 5.1 more than doubled the performance of PHP 5.0
- Versions 5.2 and 5.3 brought their own set of performance enhancements, but they were not as striking as the ones version 5.1 carried.
- The next big performance improvements come with version 5.4.
- The opcache extension came bundled with versions 5.5 and 5.6. This performance enhancements due to faster code loading when the same script is run consecutively from a Web server. However, opcache won't really show its strengths for scripts executed in CLI mode.
- PHP 7.0 is a major breakthrough in terms of performance. The Zend Engine has been completely redesigned, and we can see here the results of this work.
- PHP 7.1 introduced opcode optimizations in the opcache extension. This explains once again the gain in performance shown in the above table, when compared to 7.0.
- The Experimental JIT branch introduces yet another major breakthrough, thanks to JIT it can provide great performance improvements to your existing code, but in certain cases, you may notice no more than a few percentage points improvements in speed. In the worst cases, it can even get slower because the compilation does not result in faster code. Keep in mind that this feature is currently under development.
This section presents the results of 3 pure CPU benchmark scripts. It won't give realistic numbers when running typical PHP applications that usually perform accesses to databases or files, but I think they give an idea of the performance improvements you may expect regarding certain parts of your code.
PHP 5 brought over PHP 4 a significant improvement. The Zend Engine, which is at the heart of the interpreter, has completely been redesigned (Zend Engine 2), making room for future enhancements.I will not cover the differences between PHP 4 and PHP 5 here, but I can give a brief overview of what happened after PHP 5.0.
The following sections list the areas of improvements that occurred in successive PHP versions. Note that only the modifications affecting the core of PHP are listed here. For a more complete description, have a look at the PHP change logs for PHP 5 and PHP 7.
- Compiled variables
- Specialized executor
- Real-path cache
- Faster switch() statement handling
- Faster array functions
- Faster variable fetches
- Faster magic method invocations
- New memory manager
- Optimized array/HashTable copying
- Optimized require_once() and include_once() statements
- Small optimization on specific internal functions
- Improved compilation of HEREDOCS and compilation of interpolated strings
- Segmented VM stack
- Stackless VM
- Compile-time constants substitution
- Lazy symbol table initialization
- Real-path cache improvement
- Improved PHP runtime speed and memory usage
- Faster language parsing
- Improved PHP binary size and code startup
- Delayed HashTable allocation
- Constant tables
- Run-Time binding caches
- Interned Strings
- Improved the output layer
- Improved ternary operator performance when using arrays
- Improved VM calling convention
- OPcache integration
- Other misc. optimizations to the Zend Engine
- Optimized empty string handling, minimizing the need to allocate new empty values
PHP 7 vs PHP 5.6
Most of the improvements listed here are related to the Zend Engine:
- Core data structures re-factoring
- Better VM calling convention
- New parameters parsing API
- Yet another new memory manager
- Many improvements in VM executor
- Significantly reduced memory usage
- Improved __call() and __callStatic() functions
- Improved string concatenation
- Improved character searching in strings
PHP 7.1 Performance Improvements
- New SSA based optimization framework (embedded into opcache)
- Global optimization of PHP bytecode based on type inference
- Highly specialized VM opcode handlers
PHP 8 or PHP 7.2 Features and the PHP Next Experimental JIT branch
- Just-In-Time compiling
Benchmarking goes a little bit farther than simply running the Unix time command to measure the script's execution. This is why I went through the following steps:
Setting up the system
First I have set up a dedicated system having the following characteristics:
- A VPS with 1 virtual core running at 2.4GHz, 2Gb of RAM and two SSD drives, one for storing the operating system data, the other one used for storing the various PHP sources, binaries and reports output
- Debian Wheezy operating system, version 3.2.82-1
- Gnu C compiler version 4.9.2-10 (Debian Jessie distribution)
Although the system came bundled with Gnu C compiler version 4.7.2, upgrading to a more recent version was required. The experimental JIT branch must be compiled with Gnu C >= 4.8.
Compiling the source code
The configure script has been run using the following options before building the complete distributions :
--prefix=/usr/local/php --disable-debug --disable-phpdbg --enable-mysqlnd --enable-bcmath --with-bz2=/usr --enable-calendar --with-curl --enable-exif --enable-fpm --with-freetype-dir --enable-ftp --with-gd --enable-gd-jis-conv --enable-gd-native-ttf --with-gettext=/usr --with-gmp --with-iconv --enable-intl --with-jpeg-dir --enable-mbstring --with-mcrypt --with-openssl --enable-pcntl --with-pdo-mysql=mysqlnd --with-png-dir --with-recode=/usr --enable-shmop --enable-soap --enable-sockets --enable-sysvmsg --enable-sysvsem --enable-sysvshm --enable-wddx --with-xmlrpc --with-xsl --with-zlib=/usr --enable-zip --with-mysqli=mysqlnd
Of course, as I was compiling older versions, some of the options above needed to be disabled or replaced by others and not all extensions were available or could be compiled.
Running the benchmarks
Each benchmark has been run using the PHP CLI (Command-Line Interface) executable through a special-purpose script that follows the steps below :
- Modify the script on-the-fly to time script execution from the inside, using the microtime() function. After this modification, the benchmark script would look like :
<?php $__start__ = microtime( true ); /*** original benchmark script code here ***/ fprintf( STDERR, microtime( true ) - $__start__); ?>
This approach has been chosen to provide a consistent way of timing a script from the inside, without altering its behavior.
- Perform 2 dry runs to ensure that both the PHP executable and benchmark script contents are in the operating system cache
- Run the script 5 times and extract the min, max and average run times, as reported by the script. This article shows the average run time only, which I call "script run time".
The php.ini settings used are listed below :
engine = On short_open_tag = Off realpath_cache_size = 2M max_execution_time = 86400 memory_limit = 1024M error_reporting = 0 display_errors = 0 display_startup_errors = 0 log_errors = 0 default_charset = "UTF-8" [opcache] zend_extension=opcache.so opcache.enable=1 opcache.enable_cli=1 opcache.optimization_level=-1 opcache.fast_shutdown=1 opcache.validate_timestamps=1 opcache.revalidate_freq=60 opcache.use_cwd=1 opcache.max_accelerated_files=100000 opcache.max_wasted_percentage=5 opcache.memory_consumption=128 opcache.consistency_checks=0 opcache.huge_code_pages=1 // PHP 8/Next only opcache.jit=35 opcache.jit_buffer_size=32M
Interpreting the results
Execution has been timed using the Unix time command ; a sample output looks like this :
$ time php bench.php real: 0m1.96s user: 0m1.912s sys: 0m0.044s
The first value, real:, is the time elapsed between the invocation of the command and is termination (until you get back to the shell prompt).
The second value, user:, accounts for the time spent in user code (in our case, this is the time spent in the php executable).
The last value, sys:, accounts for the time spent in Operating System (kernel) code. This value should be minimal but can be much larger than that if your code is accessing slow devices, for example. A heavily loaded operating system can also unduly affect the value reported here.
On idle systems usually, the quantity (user+sys) should be very near from real. This is the case in our example above : user+sys = 1.956s, and real is 1.960s. The difference of 0.004s does not belong to our process : it simply means that this extra time was taken by the Operating System to perform various tasks, such as scheduling other processes.
The same script was executed on a heavily-loaded system compiling 3 different PHP versions in parallel :
$ time php bench.php real: 0m7.812s user: 0m2.02s sys: 0m0.101s
Here I clearly see that this heavy load had a significant impact on elapsed time (and maybe on system time as well).
This is why I retained in this benchmark an additional value, the Operating System overhead, which is the difference between the elapsed time and the (user + system) times.
During our pure-CPU benchmarking activity, I ensured that this value, for more than 99% of the time, was strictly less that 100 milliseconds, even when running scripts that took dozens of seconds to complete.
This article was elaborated with the close collaboration of Dmitry Stogov. Dmitry helped to clarify and review information presented in this article, so it is clear and accurate.
Dmitry Stogov was the original developer of the Turck MMCache extension that could be used since the PHP 4 days to cache PHP opcode in shared memory. After that Dmitry moved to work for Zend where he works until these days.
More recently Dmitry was the developer that started working on the PHPNG development that later became what we know as PHP 7. Dmitry also had collaboration of Xinchen Hui and Nikita Popov for PHP 7 and beyond.
Before PHP 7, in PHP 5 other core developers like Andi Gutmans, Zeev Suraski and Stas Malishev participated with major contributions that helped PHP to evolved in terms in performance. Many others have made contributions to the evolution of PHP performance evolution but I will not mention here for the sake of brevity.
As you may know, in 2016 PHP completed 21 years of development since its official birth date in June 8, 1995.
To pay homage to all those that contributed directly and indirectly to PHP development, Peter Kokot created a a stunning animation using to render a visualization using Gource of how PHP core and modules evolved across all versions since the beginning.
Peter Kokot is a well known contributor of the PHP community. He is the founder of the PHP Group on Facebook, the largest one dedicated to a single programming language now with over 140,000 members and 22 moderators.
As the PHP creator Rasmus Lerdorf used to say, "in the PHP world, nothing happens if the community does not move". I hope these words serve you as inspiration so you can do your share and also contribute to the PHP community in as many ways you can.
If you are not able to write C code to contribute to the PHP core, you can also share your PHP work in GitHub, PHP Classes, Packagist, anywhere you find it can be useful. The more places you share your best work, the better.
One special way you can contribute right now is to participate in the crowdfunding campaign of the PHP Diversity Rainbow elePHPant campaign.
This campaign is about to end less than 40 hours but it is very close to meet its goal. So please run and go there, make a pledge and participate in something bigger for the sake of the PHP community. You do not need to make a big contribution and you can still earn a nice elePHPant.
If you already made your pledge, now sit back and relax to appreciate this awesome video about the PHP development evolution.
The goal of this article was to give you an overview of the performance of the different versions of PHP, starting from 5.0 up to the latest version currently under development, using a set of known benchmark scripts.
It also provided you with a list of performance improvement areas addressed by each successive PHP version.
This article will be updated as new PHP releases are announced, and new benchmark results will be added in the future. I also expect to add some benchmark results of PHP running real world applications such as WordPress.
If you have any question or found inaccuracies, please feel free to post a comment here. Meanwhile, share this article with other developers that have interest in PHP performance.
You need to be a registered user or login to post a comment
Login Immediately with your account on: