本文是最初是来自国外的这篇:PHP Performance Evolution 2016, 感谢高可用架构公众号翻译成了中文版, 此处是转载的高可用架构翻译后的文章从PHP 5到PHP 7性能全评测(含未发布的JIT版PHP 8对比), 稍微调整了格式而成。
php
导读:PHP 是 Web 开发最经常使用的语言,每一个大版本的更新都带来很多新特性和性能提高。特别是 PHP 7.0 的发布,带来 PHP 性能飞跃。本文做者对各个 PHP 版本进行了 CPU 性能基准测试,而且带来了PHP下个大版本的消息。本文中文版由高可用架构志愿者翻译。html
自 1994 年 Rasmus Lerdorf 建立 PHP 以来, PHP 语言经历了许多改进,其中性能是开发人员在评估新版本时考虑的主要标准之一。mysql
阅读这篇文章,能够了解从 PHP 5 到 7(包括 7.1)的性能提高,同时也将了解到即将加入到 PHP 8 的试验性的 JIT 分支版本的性能。git
简介
本文将根据时间做出更新,增长更多信息和基准测试结果,包括还没有发布的新版本,以便更好地了解多年来 PHP 性能演变。若是您有更正或建议改进,请在文后留言。github
自 1994 年 Rasmus Lerdorf 建立 PHP 以来, PHP 语言经历了激烈的演进。虽然初版是一个简单的一人开发的 CGI 程序,Rasmus Lerdorf、Andi Gutmans 和 Zeev Suraski 加入了该语言的第三个版本的开发,并根本性从新设计。从那以后, PHP 开发组也建立并发展起来。sql
随着项目的发展,因为 PHP 3 自然的可扩展性, PHP 在核心和附加扩展开发的功能获得了蓬勃发展,如网络通讯,解析,缓存和数据库支持。shell
语言自己也在发展,带来了一系列的改进。这包括支持面向对象的结构,例如类,接口, traits,闭包等。数据库
对于许多开发人员来讲,仅有新功能是不够的。随着语言愈来愈受欢迎, PHP 社区对于提供更好性能,可扩展性和更少内存使用的需求愈来愈强烈。缓存
PHP 开发团队近 20 年来一直致力于解决这些需求,虽然 PHP 3 的引入大大提升了性能,但直到 Andi Gutmans 和 Zeev Suraski 引入 Zend Engine 并发布 PHP 4, PHP 的性能才开始变得正式起来。性能优化
2000 年推出的新的内存编译器和执行器模型大大提升了 PHP 的性能(提升了 5 倍甚至 10 倍),并首次被正式的 Web 应用程序和站点所使用。咱们能够说,今天 PHP 的成果远远超出了任何人在 PHP 项目诞生时的指望。
PHP 的蓬勃发展增长了改善性能的欲望。幸运的是, Zend Engine 中设计的模型为持续优化性能提供了良好的基础。
虽然 PHP 5.0 没有带来实质性的性能提高,而且在某些状况下甚至比 PHP4 更慢,一个由 Dmitry Stogov 领导的团队在社区的大力帮助下已经在后续版本中不断优化语言,在 PHP 5.6 发布的时候,在大多数状况下,性能提高在 1.5x 和 3x 之间。
2015 年 12 月, PHP 7.0 取得了重大突破。 2016 年 12 月,7.1 版本也带来了一系列加强功能。
PHP 8 性能展望
这是一个前途光明的版本,目前正在开发当中,由 Zend 的 Dmitry Stogov 主导。虽然它是基于 PHP 7.1 版本基础,但实际版本号还没有定义,因此本文称这个版本为“试验 JIT”分支下。
关键功能 JIT(Just-In-Time)编译,是一种将代码转换为另外一种字节码(好比运行它的机器 CPU 的本地代码)的技术。 JIT 可使程序运行更快。
本文涵盖了几个基准测试的结果,从 PHP 5 的第一个版本到 PHP 的试验性 JIT 分支版本,PHP 5 以前的版本性能本文不做介绍。
在写这篇文章的时候,咱们很难肯定 PHP 8 以前是否会有另外一个主要版本,好比 PHP 7.2。可是能够假设在 PHP 8 发布时,它已经包括当前试验版 JIT 分支的强大功能。
PHP 性能评估
本文只运行纯 CPU 任务脚本的基准测试(不须要I / O操做的任务例如访问文件,网络或数据库链接)。
使用的基准测试脚本以下所示:
- bench.php 可在PHP源代码的 php-src/Zend 目录
- micro_bench.php 也能够在 PHP 源代码发布的 php-src/Zend 目录中找到
- mandelbrot.php https://gist.githubusercontent.com/dstogov/12323ad13d3240aee8f1/raw/37fed3beb7e666b70e199bcf361af541b3c30d2d/b.php
基准脚本仅使用每一个PHP主要版本的最新小版本运行。所以,测试的版本以下:
- 5.0.5
- 5.1.6
- 5.2.17
- 5.3.29
- 5.4.45
- 5.5.38
- 5.6.28
- 7.0.13
- 7.1.0
- PHP-JIT(JIT实验分支)
固然,我想肯定,咱们在相同的基准上运行全部小版本,例如在 5.3.0 到 5.3.29 之间。结果是有说服力的:性能方面的主要加强不是由小版本带来的,而是主要版本号的变化,例如从 PHP 5.4 到 PHP 5.5,或从PHP 5.6 到 PHP 7。
小版本没有显示任何明显的性能改进。这意味着相同的脚本应该以相同的速度运行,不管您使用 PHP 5.4.0 仍是 PHP 5.4.45。
您能够查看基准进程部分,详细说明主机系统的设置,各个基准的运行方式以及如何解释时序结果。
纯 CPU 基准测试结果
这部分给出了每一个 PHP 版本的基准测试结果。
每一个基准列显示 3 个值:
- 时间: 执行时间,以秒和毫秒为单位
- %rel, gain:相对于之前的版本收益的执行时间。 在下面的表格中,例如,%rel。 bench.php 和版本 5.3.29 的收益是 31.89%,意味着该脚本比 5.2.17 版本运行快 31.89%。
- %abs, gain:与 PHP 5.0 相比脚本运行的收益。 若是你看看bench.php 和试验性的 JIT 分支的这个列的交集,你会注意到,对于这个特定的测试基准,PHP 8 比 PHP 5.0 快 41 倍以上。
纯CPU基准测试的结果以下所示:
CPU基准测试
(1)测试不能在 5.3 以前的版本上运行,由于它使用了还没有实现的对象功能。
(2)此列中的结果有点偏颇,由于基准须要至少 PHP 5.3 运行。把它们当成纯粹说明,由于他们不能与 PHP 5.0 性能进行比较。
(3)这是一个 mandelbrot.php 脚本的修改版本,它运行得太快,在 7.1.0 和试验 JIT 分支没法准确的统计时间,咱们在脚本中运行计算 100 次而不是 1 次。

固然,这些都是纯 CPU 的基准测试。它们不涵盖 PHP 性能的全部方面,它们可能不表明真实状况。可是结果足够显著,足以说明几个方面的问题:
- PHP 5.1 将 PHP 5.0的 性能提升了一倍多
- 5.2 和 5.3 带来了他们本身的一些性能加强,但他们没有像5.1版本那样引人注目。
- 5.4 版本是一个大的性能改进。(这里有我曾经分享过的PHP5.4的性能优化的演讲PPTPHP-5.4 Performance)
- opcache 扩展插件与 5.5 和 5.6 版捆绑在一块儿。当相同的脚本在 Web 服务器连续运行时,因为更快的代码加载会带来性能加强。可是,opcache 不会真正显示其在CLI模式下执行脚本的优点。
- PHP 7.0 是性能方面的一个重大突破。 Zend Engine 已经彻底从新设计,咱们能够在这里看到这项工做的结果。(这里有我曾经分享过的PHP7的性能优化的演讲的PPT The secret of PHP7′s Performance )
- PHP 7.1 在 opcache 扩展中引入了 opcode 优化。这再次解释了上述表格中当与 7.0 相比时,性能的增益。(这里有我曾经分享过的PHP7.1的性能优化的演讲PPTPHP 7.1′s New Features and Performance
- 试验 JIT 分支是另外一个重大突破,JIT 能够对现有代码提供很大的性能改进,但在某些状况下,你可能会注意到速度提升只有几个百分点,在最坏的状况下,它甚至可能会变慢,由于编译不会生成更快的代码。请记住,此功能目前正在开发中。
本节介绍了 3 个纯 CPU 基准测试脚本的结果。在运行一般执行的以数据库或文件访问典型场景的 PHP 应用程序时,它不会给出一样的数字,但我认为他们可以表明您对代码的某些部分指望的性能改进。
PHP 每一个版本的性能提高
PHP 5 相比 PHP 4 带来了明显的改进。 Zend Engine 是 PHP 解释器的核心,它已经彻底从新设计( Zend Engine 2),为未来的加强功能奠基了基础。本文很少介绍 PHP 4 和 PHP 5 之间的差别,只简要概述的 PHP 5.0 以后发生了什么。
如下部分列出了在后续 PHP 版本中的改进。请注意,这里仅列出影响 PHP 核心的修改。有关更完整的描述,请查看 PHP 5 和 PHP 7 的change log。
PHP 5.1
- Compiled variables
- Specialized executor
- Real-path cache
- Faster switch() statement handling
- Faster array functions
- Faster variable fetches
- Faster magic method invocations
PHP 5.2
- 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
PHP 5.3
- 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
PHP 5.4
- Delayed HashTable allocation
- Constant tables
- Run-Time binding caches
- Interned Strings
- Improved the output layer
- Improved ternary operator performance when using arrays
PHP 5.5
- Improved VM calling convention
- OPcache integration
- Other misc. optimizations to the Zend Engine
PHP 5.6
- Optimized empty string handling, minimizing the need to allocate new empty values
PHP 7.0
下面大部分列出的改进都与 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
- New SSA based optimization framework (embedded into opcache)
- Global optimization of PHP bytecode based on type inference
- Highly specialized VM opcode handlers
PHP 8 / 下一代试验性 JIT 分支版
性能如何衡量
基准化比单纯运行 Unix 时间命令来测量脚本的执行有所区别。 这就是为何我经历了如下步骤:
配置系统
首先我设置了一个具备如下特性的专用系统:
- 一个带有1个2.4GHz虚拟内核,2GB RAM和两个SSD驱动器的VPS,一个用于存储操做系统数据,另外一个用于存储各类PHPyuan dai ma,二进制文件和报告输出
- Debian Wheezy操做系统,版本3.2.82-1
- Gnu C编译器版本4.9.2-10(Debian Jessie发行版)
- 虽然系统捆绑了Gnu C编译器版本4.7.2,但须要升级到更新的版本。 试验性 JIT 分支必须用Gnu C> = 4.8编译。
编译源代码
在构建完整发行版以前,使用如下选项运行配置脚本:
- --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
注意,在编译旧版时,上面的一些选项须要被禁用或被其余替代,而且并非全部的扩展均可用或能够被编译。
运行基准测试
每一个基准测试都使用 PHP CLI 专用脚本运行,该脚本遵循如下步骤:
使用 microtime()函数从内部获取脚本执行时间。 在此修改后,基准脚本将以下所示:
- <?php
- $__start__ = microtime( true );
-
- fprintf( STDERR, microtime( true ) - $__start__);
- ?>
执行 2 次运行,以确保 PHP 可执行文件和基准测试脚本内容都在操做系统缓存中
运行脚本 5 次,并提取最小,最大和平均运行时间,如脚本报告。 本文仅显示平均运行时间,称之为“脚本运行时间”。
php.ini 文件以下所示:
- 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
分析运行结果
使用 Unix time 命令来计时,输出以下所示:
- $ time php bench.php
- real: 0m1.96s
- user: 0m1.912s
- sys: 0m0.044s
第一个值,real : 是命令开始到终止之间的时间(当你回到 shell 提示符)。
第二个值,user :说明在用户模式中花费的时间(在咱们的例子中,这是在 php 可执行文件中花费的时间)。
最后一个值 sys :说明在操做系统(内核)调用中花费的时间。这个值应该是最小的,可是若是你的代码访问缓慢的设备结果会比较大。重负载的操做系统也可能影响此处报告的值。
在空闲系统上一般,数量(user + sys)应该很是接近 real。这是在上面的例子中的状况:user + sys = 1.956s,real 是 1.960s。 0.004s 的差别不属于当前进程:它仅仅意味着操做系统执行任务所花费的额外时间,例如调度。
同一个脚本在一个负载很重的系统上执行,并行编译 3 个不一样的 PHP 版本:
- $ time php bench.php
- real: 0m7.812s
- user: 0m2.02s
- sys: 0m0.101s
在这里我清楚地看到,系统自己的重负载对使用的时间(也许在系统时间)有重大影响。
这就是为何我在这个基准中保留一个额外的值,操做系统开销,这是调用的时间和(用户+系统)时间之间的差。
在纯 CPU 基准测试活动期间,我确保在 99% 以上的时间,这个值严格小于 100 毫秒,即便运行须要几十秒钟完成的脚本。
结论
本文的目的是给你一个不一样版本PHP性能的概述,从 5.0 开始,到当前正在开发的最新版本,使用一组已知的基准脚本。
它还为您提供了由每一个连续 PHP 版本解决的性能改进方面的列表。
本文将随着新的 PHP 版本的公布而更新,而且未来会添加新的基准测试结果。 我也但愿添加一些真实世界的 PHP 应用程序,如 WordPress 的基准测试结果。
若是您有任何问题或发现不许确,请随时发表评论。 同时,与其余对 PHP 性能感兴趣的开发人员分享这篇文章。