1由来关于PHP,很多人直观的感觉PHP是一种灵活的脚本语言,库类丰富,使用方便,安全,非常适合WEB开发,但是性能较低。PHP的性能真的像大家想象的那么差吗?本文就是围绕这样一个话题展开讨论。从源代码、应用场景、基准性能、对比分析等几个方面深入分析PHP性能问题,并通过真实的性能数据说话,最终找出影响PHP模块性能的关键因素。2从原理上分析PHP的性能从原理上分析PHP的性能,主要从以下几个方面:内存管理、变量、函数、运行机制、网络模型。2.1内存管理与Nginx的内存管理方式类似,PHP内部也是基于内存池,引入了内存池的生命周期概念。在内存池方面,PHP托管了PHP脚本和扩展的所有与内存相关的操作。不同的实现和优化用于大内存和小内存的管理。在内存分配和回收的生命周期中,PHP采用了初始化申请+动态扩展+内存识别回收的机制,每次请求后直接重新屏蔽内存池。2.2变量众所周知,PHP是一种弱变量类型的语言,所以在PHP内部,所有的PHP变量都对应一个类型Zval,其定义如下:在变量方面,PHP做了很多优化工作,比如就像引用计数和作者机制上的复制一样。这样可以保证内存使用的优化,减少内存拷贝的次数(请参考http://blog.xiuwz.com/2011/11...)。在数组方面,PHP内部使用高效的哈希表来实现。2.3函数在PHP内部,所有PHP函数都转换成一个内部函数指针。比如extension中的functionZEND_FUNCTION(my_function);//类似functionmy_function(){}内部展开后就是一个函数。voidzif_my_function(INTERNAL_FUNCTION_PARAMETERS);voidzif_my_function(intht,zval*return_value,zval*this_ptr,intreturn_value_us,zend_executor_globals*executor_globals);从这个角度来看,PHP函数在内部也对应一个函数指针。2.4运行机制谈到PHP性能,很多人会说“C/C++是编译型,JAVA是半编译型,PHP是解释型”。也就是说,PHP是先动态解析代码,然后再运行代码,所以从这点来看,PHP的性能肯定是差的。确实,运行一个PHP脚本的输出确实是一个动态解析然后代码运行的过程。PHP的运行阶段也分为三个阶段:●解析。句法分析阶段。●编译。编译输出opcode中间代码。●执行。运行,动态运行输出。从上图也可以看出PHP本身内部其实有一个编译过程。其实在标准的生产环境中,基本都会用到这个特性,比如操作码缓存工具apc、eacc、xcache等。基于操作码缓存,可以达到“PHP脚本一次编译,多次运行”的效果。从这一点来看,PHP很像JAVA的半编译机制。因此,从运行机制来看,PHP的运行方式与JAVA非常相似,都是先生成中间代码,然后在不同的虚拟机上运行。2.5动态运行从上面的分析来看,PHP在内存管理、变量、函数、运行机制等方面做了很多工作,所以从原理上来看,PHP应该不会有性能问题,至少应该是更接近JAVA。但是为什么很多人觉得PHP很慢呢?尤其是在一些计算的性能对比中,总是发现PHP处理的性能比较低效。这时候就不得不说说PHP这种动态语言的特点所带来的性能问题。由于PHP是动态运行时,所有变量、函数、对象调用、作用域实现等都在执行阶段确定。.这从根本上决定了PHP性能中一些难以改变的东西:在C/C++中静态编译阶段就可以确定的变量、函数等,在PHP中需要在动态运行时确定,这也决定了PHP中间代码不能直接运行,需要在ZendEngine上运行。说到PHP变量的具体实现,不得不多说一点:hashtable。Hashtable可以说是PHP的灵魂之一。它在PHP内部被广泛使用,包括变量符号栈、函数符号栈等都是基于hashtable的。以PHP变量为例来说明PHP动态运行的特点。例如代码:这段代码的执行结果是在变量符号栈(即哈希表)中添加一项。当变量要被使用时,去变量匹配栈中查找(即变量调用配对出一个hash查找过程)。同样,对于函数调用,基本上有一个函数符号栈(哈希表)。其实在PHP的运行机制中也可以看出变量查找动态运行的一些特点。可以看到,PHP代码编译后,产生了类符号表、函数符号表、OPCODE。真正执行的时候,zendEngine会根据操作码在对应的符号表中查找并处理。在某种程度上,很难找到解决此类问题的方法。因为这是由PHP语言的动态特性决定的。但是国内外也有很多人在寻找解决方案。因为通过这个,可以从根本上彻底优化PHP。一个典型的例子就是facebook的hiphop。但是所有这样的编译优化方案基本上都牺牲了PHP的动态运行特性。当然,在具体的编译优化中可以对动态特性做一些妥协,但很难做到完全兼容。2.6网络模型目前使用PHP,理想通用的模型是使用fastcgi(PHP-FPM)。php-fpm在网络模型上类似于nginx,采用多进程Master+multi-worker的模式。php-fpm本身是基于libevent中的epoll模型。从网络模型的角度来看,这种方法和其他网络模型不会有性能差异。2.7结论从上面的分析来看,在基本的内存管理、变量、函数、运行机制、网络模型等方面,PHP本身并没有明显的性能差异,但是由于PHP的动态运行特性,PHP与其他编译的相比语言、所有变量查找、函数执行等都会有更多的CPU开销和哈希查找的额外内存开销。至于这个开销有多大,可以通过后续的benchmark性能和对比分析得到。因此,我们也大致可以看到PHP不适合的一些场景:大量的计算任务、大数据量的操作、对内存要求严格的应用场景。如果要实现这些功能,也建议通过扩展来实现,然后提供PHP调用的钩子函数。这样可以减少内部计算的变量、函数等一系列开销。3基准性能对于PHP基准性能,目前缺乏标准数据。大多数学生都有感性认识。有人认为800QPS是PHP的极限。此外,关于框架的性能及其对性能的影响的明确数字非常少。本章的目的是给出一个benchmark参考性能指标,通过数据给大家一个直观的认识。具体的benchmark性能有以下几个方面:1.裸PHP性能。完成基本功能。2.裸架性能。只做最简单的路由分发,只走核心功能。3、标准模块的基准性能。所谓标准模块的基准性能,是指业务模块功能完备的基准性能。3.1环境描述测试环境:Uname-aLinuxdb-forum-test17.db01.baidu.com2.6.9_5-7-0-0#1SMPWedAug1217:35:51CST2009x86_64x86_64x86_64GNU/LinuxRedHatEnterpriseLinuxASrelease4(NahantUpdate3)8Intel(R)Xeon(R)CPUE5520@2.27GHz软件相关:Nginx:nginx版本:nginx/0.8.54builtbygcc3.4.520051201(RedHat3.4.5-2)Php5:(使用php-fpm)PHP5.2.8(cli)(built:Mar6201117:16:18)Copyright(c)1997-2008ThePHPGroupZendEnginev2.2.0,Copyright(c)1998-2008ZendTechnologieswitheAcceleratorv0.9.5.3,版权所有(c)2004-2006eAccelerator,作者eAcceleratorbingo2:PHP框架。其他说明:目标机部署方式:脚本。测试压力机和目标机独立部署。3.2裸PHP性能最简单的PHP脚本。init();$objAction->execute();?>Acitons/indexAction.php中代码如下3.3裸PHP框架性能为了与3.2进行对比,基于bingo2框架实现了类似的功能。代码如下'./actions',));$objFrontController->dispatch();?>From从测试结果可以看出,框架虽然有一定的消耗,但是对整体性能影响很小。3.4标准PHP模块的基准性能所谓标准PHP模块是指一个PHP模块必须具备的特定的基本功能:●路由分发。●自动加载。●LOG初始化&通知日志打印。所有UI请求都有一个标准日志。●错误处理。●时间校正。●自动计算每个阶段的耗时成本。●代码识别和代码转换。●标准配置文件的解析调用使用了bingo2的自动代码生成工具,生成了一个标准的测试PHP模块:test。3.5结论从测试数据的结论来看,PHP本身的性能还是可以接受的。基准性能可以达到数千甚至超过W的QPS。至于为什么大多数PHP模块性能不好,其实这个时候更需要找出站长博客系统的瓶颈,而不是简单的说OK,PHP不好,还是改成C吧。(下一章会用一些例子来对比,使用C不一定有特别的优势。)通过benchmark数据,可以得出以下具体结论:1.PHP本身的性能也很不错.简单功能下可以达到5000QPS(50CPUIDLE),极限也可以超过W。2.PHP框架本身对性能的影响非常有限。尤其是在某些业务逻辑和数据交互的情况下,几乎可以忽略不计。3.对于一个标准的PHP模块,基准性能可以达到2000QPS(80个cpu空闲)。4PHP和C性能对比分析很多时候,当你发现PHP模块性能不好的时候,就说“好吧,我们用C重写吧”。在公司里,用C/C++写业务逻辑模块的现象比比皆是。前几年几乎都是用C写的,那时候大家写的真是蛋疼:调试难,别谈敏捷。那么,本章要讨论的话题之一就是:用C写的业务逻辑和用PHP写的业务逻辑模块的性能对比,用真实的数据来说话。4.1前提为什么要特别提到这个前提?因为在理想情况下,一个函数用PHP实现,性能肯定比不上用理想C写的,这个前提需要特别注意。但是为什么要打扰呢?因为在现实中,能写出多少优秀的C程序,并能在频繁的修改下达到完全的高性能?而在实际应用中,C所达到的性能真的比PHP好几倍吗?这些目前没有确切的数据可以证明。因此,本章的比较是立足于真实情况,用真实的数据来说话。4.2真实业务模块PHP模块VSC模块4.2.1业务模块介绍一个真实案例,该业务模块的流量高达亿级。这个业务模块的功能很简单,上层是web服务器,下游是各个数据模块。它们都是基于socket进行数据交互的。该业务模块的主要工作模型是:响应web服务器的请求,根据请求从各个后端数据模块中读取相应的数据,并根据数据输出最终的HTML页面返回给网络服务器。为方便后续介绍,定义CUI为C语言实现的模块,PHPUI为PHP实现的模块。4.2.2C/C++模块性能数据结果2009年选择了新的C/C++框架进行模块重构。在重构的时候,连接到这个模块的后端数据模块的大小是5-7。基于C/C++模块,最终测试数据分为两部分:1.性能对比测试。基于当时的线上压力,进行了真实数据的性能测试。所以当时只测试了一个压力数据如下:Pressure:210QPSCPU(IDLE):84.18二、极限性能测试1、测试模型为:CUI只连接一个核心数据模块,其他数据模块完全关闭.三、极限性能测试2、测试模型为:CUI连接后端1个核心数据模块,3个数据模块,其他数据模块不连接。4.2.3PHP实现模块性能测试数据到2011年,基于2009的CUI基本达到了代码不关心维护的地步。而此时CUI的极限性能不到600QPS(主要原因是随着项目的发展,后端数据模块增加到14个)。基于此,决定采用PHP方案重写整个模块,制作出最终的pbui模块。性能测试结果分为两种:1.PHPUI连接了一个核心模块。2.PHPUI连接到后端的所有模块(14)。4.2.4数据对比结论由于PHPUI和CUI的业务逻辑和测试方法不完全相同,所以提取一些可以普遍对比的点进行整理。具体对比数据如下:从上面的对比数据来看,在实际的业务项目中,PHPUI的性能并不比CUI差。这不是由单个模块验证的。在部门中,我们有许多模块从C/C++迁移到PHP。从迁移结果来看,没有质的性能下降。大部分模块迁移后性能指标非常接近。这时候,我们就需要思考一下,为什么会这样呢?细分的话,有两个问题:1、为什么PHPUI在实际业务项目中的性能并不比CUI差多少?2、为什么基准PHP性能那么高,在80CPU的情况下2000QPS,而在真正的PHP模块中只有200QPS?其实这两个问题也可以归结为一个原因:在实际业务项目中,影响性能的不是使用的语言,而是业务相关的部分,比如socket交互的次数,比如字符串处理,比如网络交互数据包大小。好的。那么接下来的关键就是找出影响性能的关键因素。4.2.5影响PHP模块性能的关键因素通过前面的分析,我们得出结论,影响前端PHP模块性能的关键因素不是语言本身(不管是PHP/JAVA/C还是不是)。那么影响PHP业务模块性能的关键因素在哪里呢?考虑到系统中打印了一系列日志,CPU时间消耗是衡量项目性能的关键点之一。通过分析日志中请求的耗时分布,我们可以大致看出重点。在我们的系统中,CPU耗时主要打印出以下几个方面:1.请求的总时间。2.请求关键功能的性能,其中所有socket交互都有耗时计算。3.模板渲染也是好东西的一个重点。在前面的分析中,我们基本判断socket和字符串处理是重点之一,下面通过数据来验证一下。从一个模块中提取指定数量的日志,综合分析得到如下数据:由此可见,在一个业务模块中,socket数据交互影响最大,其次是大量的字符串处理.具体细分为以下因素:socket交互次数、socket交互包大小、socket交互响应时间、字符串处理。4.2.6结论通过以上分析,可以得出以下结论:在前端业务模块中,PHP语言本身不会成为性能瓶颈。因为影响性能的几个关键因素是:●网络交互的数量。●网络交互数据的大小,包括数据打包和解包的开销。●网络交互响应时间。●广泛的字符串处理。5最后的结论通过以上三章的具体分析,可以得出以下结论:1.从PHP实现原理来看,PHP是一种半编译语言,在各个方面都做了很多优化工作,它不会单独存在。明显的性能问题。但是由于动态语言的特性,PHP需要运行在ZendEngine虚拟机上,在变量查找、函数调用、作用域切换等各方面都需要一些额外的开销。2、从PHP的benchmark性能来看,PHP本身不会有明显的资源消耗,单机QPS很容易超过W,PHP框架本身不会对业务系统的性能产生临界影响。3、从实际应用场景来看,基于C语言的模块可能并不比基于PHP的模块效率高多少。因为在实际应用场景中,更多的性能开销在于网络数据交互和字符串处理。语言的微小性能差异不会成为瓶颈。据此可以得出结论,大部分基于C语言的业务系统都可以考虑迁移到PHP。一方面可以快速开发,另一方面也不会有性能问题。
