2

PHP作为Web开发中应用最广泛的开源脚本语言,凭借库类丰富,使用简单,安全等特点,成为Facebook和BAT等互联网巨头和全球超过70%网站的主要开发语言,然而性能问题是PHP一直以来饱受诟病的,来自PHP/PECL开发组的高驰涛同学将为我们带来他对PHP性能优化方面的思考和建议。
Xxx

本期主讲:高驰涛,Neeke Gao,PHP/PECL开发组成员,同时是SeasLog,JsonNet-PHP的作者,目前在云智慧担任架构师。
今天主要从PHP的发展和性能优化历史,来谈PHP语言在性能优化方面所做过的努力,以及我们的一些研究成果。
谈起PHP,我们先来了解几个数字:
Xxx

1995年, PHP发布了第一个版本,这要感谢PHP之父Rasmus Lerdorf的辛苦努力。PHP在问世之初是Personal Home Page的缩写,那时候只是被当作制作个人网页的一门简单语言;后来随着互联网的发展以及开源社区的贡献,PHP逐步成长成为一门被广泛应用的高级语言,其英文含义也进化为PHP: Hypertext Preprocessor,即超级文本预处理语言。

官方统计数字显示,全球排名前1000万的网站应用中,有81.9%是使用PHP语言开发的。而且,每天有416个新增PHP网站进入全球前1000万排名,数量还在不断增加。发展到今天,PHP经历了4,5,7几个非常重大的版本迭代,最新的release版本是7.0.6。在二十多年的发展过程中,应用最广泛的PHP版本是PHP 5,同时也是目前全球互联网上应用最为广泛的一个语言版本。

为什么没有PHP6,而是直接从PHP5到PHP7,这里面有一个小故事。目前的PHP5.6,其实就是原预计发布的PHP6,但由于各项改动不足以支撑一个全新的PHP分支,组织内投票没有通过发布,而最终作为了PHP5.6进行发布。

PHP7又称为PHP NG,即“PHP Next Generation(下一代PHP)”,在性能和稳定性方面做了非常大的提升。为什么这么说呢,要从PHP的业务模型说起。

1、PHP Transaction Module业务模型
下面我先带大家过一下PHP5几个重大版本改动中的性能优化点,下面这张图是PHP整个运行过程中的几个关键点。
Xxx

在PHP运行过程中,在Script Entry接收到请求之后。
首先经过语法解析器(Parser),此时会抛出一些致命的语法错误或警告(后续的版本改动中,会有一些致命错误转换为exception抛出);
其次经过编译器(Compiler),这部分内容中会进行更高级的解析,并测试将PHP高级语言编译成OpCode;

然后交给Execute进行执行;执行过程中发现有新的文件或类被引入,会再进行Compiler,往复执行。

从这里可以看出,其实PHP也是“编译型”语言,只不过是每次请求都编译,或称动态编译。那么,后续的优化过程中就可以进行针对性优化,即:将编译后的内容缓存起来,直接执行,以节省编译时间。

了解一点PHP的同学都知道,PHP通常被认为是简单、低效,这并不是没有道理。我们来看一段PHP源码,从而向大家解释为什么“简单”,为什么“低效”。
Xxx

这段结构是PHP5及以前版本中所有变量和方法执行的核心,这是通用的变量存储结构。可以看到,同一个变量能够同时有多个属性,或long,或double,或char,或int,或array,或Object。这给工程师以充分的自由度,同样的一个数字,或字符串,不需要预设类型,就可以直接生成并使用。这就是为什么“简单”,这是对工程师来说的。

而同时丢给Zend引擎(解析执行引擎)的,有着大量的负担,因为需要引擎在每一次使用或运算时,都要不停的判断类型究竟是什么类型属性。这就是为什么“低效”的一个最直接原因。

2、PHP Performance Difficulty 性能之坑
对于一个使用PHP开发的应用来说,经过大量的实践,可以总结出4个最影响性能的点:
图片描述

IO, Memory, CPU, NetWork,是不是非常眼熟?没错,这其实并不单单是PHP语言的性能瓶颈,任何一门语言,或者就目前我们所处的世界中,凡是使用计算机语言开发的服务或应用,都存在这4个瓶颈。

3、Optimize from PHP5 to PHP7 优化之旅
下面就为大家介绍一下PHP5围绕以上4个点所做的一些性能优化。在聊具体的性能优化方法之前,我们可以抛开之前的知识储备,打开脑洞,发散思维,思考应该如何入手。每个人的想法完全不一样,我的想法是这样的:

会不会是PHP本身的运行模型(或业务模型)或者Zend引擎设计存在问题?
有没有可能将PHP语言在发布运行之前进行预编译,把它转变成Bytecode?
有没有可能通过一种解析器把PHP转变成C或C++语言,然后进行make或build?
能不能开发另一套解析运行引擎?
会不会出现另一套完全不一样的运行模型(或业务模型)?
等等。

这些想法在PHP社区内屡见不鲜,并不仅仅是我一个人在这么想,非常多PHP资深专家也在思考,而且在各个方向上都有一些开源的工具或服务发布了。由于PHP5有非常多版本,这里我们仅就应用最广的PHP5.4和PHP5.5-5.6版本来简要说明性能优化的几个点。
图片描述
PHP5.4 最主要的性能改进是在数组的生成和应用上面,一个简单的延后分配节省了非常大量的内存,因为多数情况数组在直接引用上并不进行运算,从而并不进行内存建立与分配工作。另外,PHP5.4在运行时中添加了Literal Table和Binding Cache操作,这两个改动相对上面延后分配内存是更复杂一些的,它在运行过程中节省了大量的CPU运算。
图片描述

PHP5.5及5.6放在一起来说,因为它们真的很像,而且在目前PHP5整个大分支的维护过程中,一般是在PHP5.5上进行,然后把相关修改向5.6同步。5.5和5.6最重要的一个性能改进,是使用了编译Cache。前面我们了解了PHP的运行过程中会存在“编译->OpCode->执行”的过程,而将编译结果缓存起来是一个重要的性能优化点,OpCache就是做这个工作的。OpCache从此进入了PHP的内核,而不再是一个第三方扩展。

同时Zend引擎还有同样一个Cache优化扩展版本产生,即Zend O+,可以认为是Zend的Plus版本,在内存和CPU上,都做了一些改进。
图片描述

最后来看看PHP7的几个优化点:PHP7直接修改了Zend Zval结构,这算得上是一个很大改动。同时在PHP7中,数组的结构也变得更加的不一样,这又是一个伤筋动骨的改动。官方数据中PHP7较PHP5.6有近两倍的性能提升,主要就集中在这两个点上。
看一下PHP源码:
图片描述
在一个Zval结构中,一个变量仍然可以属于各种类型的,但在使用的时候,可以快速判断是哪种类型,因为不同的类型会存储在不同的指针中;而且内存使用也由于不同的指针类型而大量地降低;由于内存使用大量降低,从而为CPU更快地寻址和操作带来了更好的可能性。所以有了Zval的结构变动带来35%性能提升的结论。
图片描述

同一个数组,在PHP5中的内存使用是跳跃不连贯的,而在PHP7中是连续有序的,这使得内存使用和CPU使用都更加地高效,数组的结构变动,给PHP7带来了接近50%的性能提升。
4、PHP Performance Tools 工具推荐

前面在我们发散思维时列举了很多PHP性能优化的点,几个开源社区中已经发布过针对这些的优化工具,给大家推荐一些:
Facebook发布的HHVM:
图片描述

HHVM (HipHop Virtual Machine)类似于C#的CLR和Java的JVM,会将PHP代码转换成高级别的字节码(通常称为中间语言),然后在运行时通过即时(JIT)编译器将这些字节码转换为x64的机器码。
下面是一些在开发调试过程中经常使用的工具和软件服务:
图片描述
图片描述
此类开发工具和服务很多,可以为开发者和运维同学节省大量的定位和解决问题时间。
以我开发的SeasLog为例,由于使用内存buffer/批量IO,替代直接IO,可以直接为服务的QPS能力提升带来强劲动力,下图是SeasLog和log4php的性能对比:
图片描述
最后为东家打个小广告,云智慧的APM应用性能管理产品透视宝www.toushibao.com,可以提供更为强劲的性能管理分析的商业支持方案,目前支持包括PHP,Java,DotNet,Python,IOS,Android在内各种主流应用的性能监测和管理分析能力,欢迎大家交流体验。


CloudwiseAPM
238 声望39 粉丝