PHP性能分析与实验性能的宏观分析
此前,阅读过了很多关于PHP性能分析的文章,不过写的都是一条一条的规则,而且,这些规则并没有上下文,也没有明确的实验来体现出这些规则的优势,同时讨论的也侧重于一些语法要点。本文就改变PHP性能分析的角度,并通过实例来分析出PHP的性能方面需要注意和改进的点。
对PHP性能的分析,我们从两个层面着手,把这篇文章也分成了两个部分,一个是宏观层面,所谓宏观层面,就是PHP语言本身和环境层面,一个是应用层面,就是语法和使用规则的层面,不过不仅探讨规则,更辅助以示例的分析。
宏观层面,也就是对PHP语言本身的性能分析又分为三个方面:
PHP作为解释性语言性能有其天然的缺陷
PHP作为动态类型语言在性能上也有提升的空间
当下主流PHP版本本身语言引擎性能
一、PHP作为解释性语言的性能分析与提升
PHP作为一门脚本语言,也是解释性语言,是其天然性能受限的原因,因为同编译型语言在运行之前编译成二进制代码不同,解释性语言在每一次运行都面对原始脚本的输入、解析、编译,然后执行。如下是PHP作为解释性语言的执行过程。
如上所示,从上图可以看到,每一次运行,都需要经历三个解析、编译、运行三个过程。
那优化的点在哪里呢?可以想见,只要代码文件确定,解析到编译这一步都是确定的,因为文件已不再变化,而执行,则由于输入参数的不同而不同。在性能优化的世界里,至上绝招就是在获得同样结果的情况下,减少操作,这就是大名鼎鼎的缓存。缓存无处不在,缓存也是性能优化的杀手锏。于是乎OpCode缓存这一招就出现了,只有第一次需要解析和编译,而在后面的执行中,直接由脚本到Opcode,从而实现了性能提速。执行流程如下图所示:
相对每一次解析、编译,读到脚本之后,直接从缓存读取字节码的效率会有大幅度的提升,提升幅度到底有多大呢?
我们来做一个没有Opcode缓存的实验。20个并发,总共次请求没有经过opcode缓存的请求,,得到如下结果:
其次,我们在服务器上打开Opcode缓存。要想实现opcode缓存,只需要安装APC、ZendOPCache、eAccelerator扩展即可,即使安装了多个,也只启用其中一个。注意的是,修改了php.ini配置之后,需要重新加载php-fpm的配置。
这里分别启用APC和ZendOPCache做实验。启用APC的版本。
可以看到,速度有了较大幅度的提升,原来每个请求ms,每秒处理请求个,启用了APC之后68ms,每秒处理请求个,提升速度将近40%。
在启用了ZendOpcache的版本中,得到同APC大致相当的结果。每秒处理请求个,每请求耗时68.5ms。
从上面的这个实验可以看到,所用的测试页面,有40ms以上的时间花在了语法解析和编译这两项上。通过将这两个操作缓存,可以将这个处理过程的速度大大提升。
这里附加补充一下,OpCode到底是什么东东,OpCode编译之后的字节码,我们可以使用bytekit这样的工具,或者使用vldPHP扩展来实现对PHP的代码编译。如下是vld插件解析代码的运行结果。
可以看到每一行代码被编译成相应的OpCode的输出。
二、PHP作为动态类型语言的性能分析与改进
第二个是PHP语言是动态类型的语言,动态类型的语言本身由于涉及到在内存中的类型推断,比如在PHP中,两个整数相加,我们能得到整数值,一个整数和一个字符串相加,甚至两个字符串相加,都变成整数相加。而字符串和任何类型连接操作都成了字符串。
?php$a=10.11;$b=30;var_dump($a+$b);var_dump(10+$b);var_dump(10+20);var_dump(10+20);
运行结果如下:
float(40.11)int(40)int(30)int(30)
语言的动态类型为开发者提供了方便,语言本身则会因为动态类型而降低效率。在Swift中,有一个特性叫类型推断,我们可以看看类型推断会带来多大的一个效率上的差别呢?对于需要类型推断与不需要类型推断两段Swift代码,我们尝试编译一下看看效果如何。第一段代码如下:
这是一段Swift代码,字典只有14个键值对,这段代码的编译,9分钟了还没有编译完成(5G内存,2.4GHzCPU),编译环境为Swift1.2,Xcode6.4。
但是如果调整代码如下:
也就是加上了类型限定,避免了planeLocation的类型推断。编译过程花了2S。
可见,作为动态类型附加的类型推断操作极大地降低了程序的编译速度。当然,这个例子有点极端,用Swift来类比PHP也不一定合适,因为Swift语言本身也还在不断的进化过程中。本例子只是表明在编程语言中,如果是动态类型语言,就涉及到对动态类型的处理,从编译的角度讲是会受影响的。
那么作为动态类型的PHP的效率如何提升呢?从PHP语言本身这个层面是没有办法解决的,因为你怎么写也是动态类型的代码。解决办法就是将PHP转化为静态类型的表示,也就是做成扩展,可以看到,鸟哥的很多项目,比如Yaf框架,都是做成了扩展的,当然这也是由于鸟哥是C高手。扩展由于是C或者C++而写,所以不再是动态类型,又加之是编译好的,而C语言本身的效率也会提升很多。所以效率会大幅度提高。
下面我们来看一段代码,这段代码,只是实现了简单的素数运算,能计算指定值以内的素数个数,用的是普通的筛选法。现在看看扩展实现,跟PHP原生实现的效率差别,这个差别当然,不仅仅是动态类型和编译类型的差别,还有语言效率的差别。
首先是用纯PHP写成的算法,计算万以内的素数个数,耗时在33s上下,实验了三次,得到的结果基本相同。
其次,我们将这个求素数个数的过程,编写成了PHP扩展,在扩展中实现了getprimenumbers函数,输入一个整数,返回小于该整数的素数。得到的结果如下,这个效率的提升是非常惊人的,在1.4s上下即返回。速度提升20倍以上。
可以想见,静态和编译类型的语言,其效率得到了惊人的提升。本程序的C语言代码如下:
PHP_FUNCTION(get_prime_numbers){longvalue;if(zend_parse_parameters(ZEND_NUM_ARGS()TSRMLS_CC,l,value)==FAILURE){return;}int*numbers=(int*)malloc(sizeof(int)**);memset(numbers,0x0,*);intnum=2;numbers[0]=2;numbers[1]=3;boolflag=true;doublef=0;inti=0;intj=0;for(i=5;i=value;i+=2){flag=true;f=sqrt(i);for(j=0;jnum;j++){if(i%numbers[j]==0){flag=false;break;}if(numbers[j]f){break;}}if(flag){numbers[num]=i;num++;}}free(numbers);RETURN_LONG(num);}
三、PHP语言本身底层性能引擎提升
第三个性能优化层面是语言本身的性能提升,这个就不是我们普通开发者所能做的了。在PHP7以前,寄希望于小版本的改进,但是改进幅度不是非常的显著,比如PHP5.3、PHP5.4、PHP5.5、PHP5.5对同一段代码的性能比较,有一定程度的进步。
PHP5.3的版本在上面的例子中已讲过,需要33s左右的时间,我们现在来看别的PHP版本。分别运行如下:
PHP5.4版,相较5.3版已经有一定程度的提升。快6秒左右。
PHP5.5版在PHP5.4的基础上又进了一步,快了6S。
PHP5.6反而有些退步。
PHP7果真是效率提升惊人,是PHP5.3的3倍以上。
以上是求素数脚本在各个PHP版本之间的运行速度区别,尽管只测试了这一个程序,也不是特别的严谨,但是这是在同一台机器上,而且编译configure参数也基本一样,还是有一定可比性的。
在宏观层面,除了上面的这些之外,在实际的部署过程中,对PHP性能的优化,还体现为要减少在运行中所消耗的资源。所以FastCGI模式和mod_php的模式比传统的CGI模式也更为受欢迎。因为在传统的CGI模式中,在每一次脚本运行都需要加载所有的模块。而在程序运行完成了之后,也要释放模块资源。如下图所示:
而在FastCGI和mod_php模式中,则不需要如此。只有php-fpm或者Apache启动的时候,需要加载一次所有的模块,在具体的某次运行过程中,并不需要再次加载和释放相关的模块资源。
这样程序性能的效率得到了提升。以上就是有关PHP宏观层面的性能优化的分析,在本文的第二部分我们将探讨应用方面的PHP优化准则。敬请期待!
本文首发于OneAPM官方博客
作者:优才学院
优才学院
优才学院(原优才网),是国内唯一一家专注于多方向全栈工程师培养的在线教育网站。由原开心网早期创始成员、资深架构师伍星于年底创立,年9月获得创新工场天使投资。目前,在Web、iOS、JS、UI培训领域有着重要的影响力,培养的Web全栈工程师更是在国内有着极高的知名度,毕业的学员大多都在其公司核心技术岗位担任要职,被业界称为“互联网核心技术岗位人才培养”的摇篮。
·END·
优才学院全栈工程师培训领导品牌
版权所有:Copyright ? 2012-12-20 php发展_php应用_php开发

电话:
当前时间: