1缘起
对于PHP,很多人的直观感觉是PHP是一种灵便的脚本语言,库类丰盛,应用简略,平安,非常适合WEB开发,但性能低下。PHP的性能是否真的就如同大家的感觉一样的差呢?本文就是围绕这么一个话题来进行探讨的。从源码、利用场景、基准性能、比照剖析等几个方面深入分析PHP之性能问题,通过实在的性能数据来谈话,最终找出影响PHP模块性能的关键因素。
2从原理剖析PHP性能
从原理剖析PHP的性能,次要从以下几个方面:内存治理、变量、函数、运行机制、网络模型来进行剖析。
2.1内存治理
相似Nginx的内存治理形式,PHP在外部也是基于内存池,并且引入内存池的生命周期概念。在内存池方面,PHP对PHP脚本和扩大的所有内存相干操作都进行了托管。对大内存和小内存的治理采纳了不同的实现形式和优化。在内存调配和回收的生命周期内,PHP采纳一次初始化申请+动静扩容+内存标识回收机制,并且在每次申请完结后间接对内存池进行从新mask。
2.2变量
总所周知,PHP是一种弱变量类型的语言,所以在PHP外部,所有的PHP变量都对应成一种类型Zval,其中具体定义如下:
在变量方面,PHP做了大量的优化工作,比如说Reference counting和copy on writer机制。这样可能保障内存应用上的优化,并且缩小内存拷贝次数(请参考http://blog.xiuwz.com/2011/11…)。在数组方面,PHP外部采纳高效的hashtable来实现。
2.3函数
在PHP外部,所有的PHP函数都回转化成外部的一个函数指针。比如说扩大中函数
ZEND_FUNCTION ( my_function );//相似function my_function(){}
在外部开展后就会是一个函数
void zif_my_function ( INTERNAL_FUNCTION_PARAMETERS );
void zif_my_function(
int ht,
zval * return_value,
zval * this_ptr,
int return_value_used,
zend_executor_globals * executor_globals
);
从这个角度来看,PHP函数在外部也是对应一个函数指针。
2.4运行机制
在话说PHP性能的时候,很多人都会说“C/C++是编译型,JAVA是半编译型,PHP是解释型”。也就是说PHP是先动静解析再代码运行的,所以从这个角度来看,PHP性能必然很差。
确实,从PHP脚本运行来输入,确实是一个动静解析再代码运行的过程。
PHP的运行阶段也分成三个阶段:
●Parse。语法分析阶段。
● Compile。编译产出opcode两头码。
● Execute。运行,动静运行进行输入。
通过上图也能够看出,其实在PHP外部自身也是存在编译的过程。事实上,在规范的生产环境中,也都基本上利用了这个特点,比如说opcode cache工具apc、eacc、xcache等等。基于opcode cache,能到做到“PHP脚本编译一次,屡次运行”的成果。从这点上,PHP就和JAVA的半编译机制十分相似。
所以,从运行机制上来看,PHP的运行模式和JAVA是十分相似的,都是先产生两头码,而后运行在不同虚拟机上。
2.5动静运行
从下面的几个剖析来看,PHP在内存治理、变量、函数、运行机制等几个方面都做了大量的工作,所以从原理来看,PHP不应该存在性能问题,性能至多也应该和JAVA比拟靠近。
但为什么还有很多人感觉PHP慢呢?尤其是一些计算量的性能比照上,总发现PHP解决的性能绝对比拟低效。这个时候就不得不谈PHP动静语言的个性所带来的性能问题了,因为PHP是动静运行时,所以所有的变量、函数、对象调用、作用域实现等等都是在执行阶段中才确定的。这个从根本上决定了PHP性能中很难扭转的一些货色:在C/C++等可能在动态编译阶段确定的变量、函数,在PHP中须要在动静运行中确定,也就决定了PHP两头码不能间接运行而须要运行在Zend Engine上。
说到PHP变量的具体实现,又不得不说一个货色了:hashtable。Hashtable能够说在PHP灵魂之一,在PHP外部宽泛用到,蕴含变量符号栈、函数符号栈等等都是基于hashtable的。
以PHP变量为例来阐明下PHP的动静运行特点,比如说代码:
- <?php
- $var = “hello, blog.xiuwz.com”;
- ?>
该代码的执行后果就是在变量符号栈(是一个hashtable)中新增一个项
当要应用到该变量时候,就去变量合乎栈中去查找(也就是变量调用对出了一个hash查找的过程)。
同样对于函数调用也基本上相似有一个函数符号栈(hashtable)。
其实对于动静运行的变量查找特点,在PHP的运行机制中也能看出一些。
能够看出,PHP代码在compile之后,产出的了类符号表、函数符号表、和OPCODE。在真正执行的时候,zend Engine会依据op code去对应的符号表中进行查找,解决。
从某种程度上,在这种问题的上,很难找到解决方案。因为这是因为PHP语言的动静个性所决定的。然而在国内外也有不少的人在寻找解决方案。因为通过这样,可能从根本上齐全的优化PHP。典型的列子有facebook的hiphop。
但所有的这种编译优化计划,都基本上是就义了PHP动静运行的个性。当然能够在具体的编译优化中去对动静个性做一些折中,但很难做到完完全全的兼容。
2.6网络模型
目前采纳PHP的形式,比拟现实和通用的模式是采纳fastcgi(PHP-FPM)。Php-fpm在网络模型上比拟相似nginx,采纳了多过程Master+多worker的模式。Php-fpm自身是基于libevent中的epoll模型。从网络模型来看,该形式也不会和其余网络模型存在性能差别。
2.7论断
从下面剖析来看,在根底的内存治理、变量、函数、运行机制、网络模型方面,PHP自身并不会存在显著的性能差别,但因为PHP的动静运行个性,决定了PHP和其余的编译型语言相比,所有的变量查找、函数运行等等都会多一些hash查找的CPU开销和额定的内存开销,至于这种开销具体有多大,能够通过后续的基准性能和比照剖析得出。
因而,也能够大体看出PHP不太适宜的一些场景:大量计算性工作、大数据量的运算、内存要求很严格的利用场景。如果要实现这些性能,也倡议通过扩大的形式实现,而后再提供钩子函数给PHP调用。这样能够减低外部计算的变量、函数等系列开销。
3基准性能
对于PHP基准性能,目前短少规范的数据。大多数同学都存在理性的意识,有人认为800QPS就是PHP的极限了。此外,对于框架的性能和框架对性能的影响很没有响应的权威数字。
本章节的目标是给出一个基准的参考性能指标,通过数据给大家一个直观的理解。
具体的基准性能有以下几个方面:
1、 裸PHP性能。实现根本的性能。
2、 裸框架的性能。只做最简略的路由散发,只走通外围性能。
3、 规范模块的基准性能。所谓规范模块的基准性能,是指一个具备残缺服务模块性能的基准性能。
3.1环境阐明
测试环境:
Uname -a
Linux db-forum-test17.db01.baidu.com 2.6.9_5-7-0-0 #1 SMP Wed Aug 12 17:35:51 CST 2009 x86_64 x86_64 x86_64 GNU/Linux
Red Hat Enterprise Linux AS release 4 (Nahant Update 3)
8 Intel(R) Xeon(R) CPU E5520 @ 2.27GHz
软件相干:
Nginx:
nginx version: nginx/0.8.54 built by gcc 3.4.5 20051201 (Red Hat 3.4.5-2)
Php5:(采纳php-fpm)
PHP 5.2.8 (cli) (built: Mar 6 2011 17:16:18)
Copyright (c) 1997-2008 The PHP Group
Zend Engine v2.2.0, Copyright (c) 1998-2008 Zend Technologies
with eAccelerator v0.9.5.3, Copyright (c) 2004-2006 eAccelerator, by eAccelerator
bingo2:
PHP框架。
其余阐明:
指标机器的部署形式:
脚本。
测试压力机器和指标机器独立部署。
3.2裸PHP性能
最简略的PHP脚本。
- <?php
- require_once ‘./actions/indexAction.php’;
- $objAction = new indexAction();
- $objAction->init();
- $objAction->execute();
- ?>
Acitons/indexAction.php外面的代码如下
- <?php
- class indexAction
- {
- public function execute()
- {
- echo ‘hello, world!’;
- }
- }
- ?>
3.3裸PHP框架性能
为了和3.2的比照,基于bingo2框架实现了相似的性能。代码如下
- <?php
- require_once ‘Bingo/Controller/Front.php’;
- $objFrontController = Bingo_Controller_Front::getInstance(array(
- ‘actionDir’ => ‘./actions’,
- ));
- $objFrontController->dispatch();
- ?>
从该测试后果能够看出:框架尽管有肯定的耗费,但对整体的性能来说影响是十分小的。
3.4规范PHP模块的基准性能
所谓规范PHP模块,是指一个PHP模块所必须要具体的基本功能:
●路由散发。
●主动加载。
●LOG初始化&Notice日志打印。所以的UI申请都一条规范的日志。
●错误处理。
●工夫校对。
●主动计算每个阶段耗时开销。
●编码辨认&编码转化。
●规范配置文件的解析和调用
采纳bingo2的代码主动生成工具产生规范的测试PHP模块:test。
3.5论断
从测试数据的论断来看,PHP自身的性能还是能够的。基准性能齐全可能达到几千甚至上W的QPS。至于为什么在大多数的PHP模块中体现不佳,其实这个时候更应该去找出站长博客零碎的瓶颈点,而不是简略的说OK,PHP不行,那咱们换C来搞吧。(下一个章节,会通过一些例子来比照,采纳C来解决不见得有特地的劣势)
通过基准数据,能够得出以下几个具体的论断:
1、 PHP自身性能也很不错。简略性能下可能达到5000QPS(50CPU IDLE),极限也能过W。
2、 PHP框架自身对性能影响十分无限。尤其是在有肯定业务逻辑和数据交互的状况下,简直能够疏忽。
3、 一个规范的PHP模块,基准性能可能达到2000QPS(80 cpu idle)。
4PHP与C性能比照剖析
很多时候,大家发现PHP模块性能不行的时候,就来一句“ok,咱们采纳C重写吧”。在公司内,采纳C/C++来写业务逻辑模块的景象到处都有,在前几年甚至简直全部都是采纳C来写。那时候大家写的真是一个苦楚:调试难、麻利不要谈。
那么,本章节要议论的一个话题就是:C写的业务逻辑和PHP写的业务逻辑模块进行性能比照,采纳实在的数据来谈话。
4.1前提
为什么要特地说出这个前提呢?因为在现实状况下,一个性能采纳PHP实现,该性能铁定不可能比现实的C写进去好。这个前提须要特地留神。
但为什么还要比照呢?因为在现实情况下,能写出十分优良的C程序,并且在频繁批改的状况下还能做到齐全高性能的又有几个呢?并且在事实的利用中C实现的性能是否真的全都都比PHP要好好几倍呢?这些目前都没有确切的数据来论证。
所以,本章节的比照是基于事实中的状况来进行的,并采纳实在数据来谈话。
4.2 实在业务模块PHP模块 VS C模块
4.2.1业务模块介绍
一个实在的案列,该业务模块的流量高达数十亿。
该业务模块性能非常简单,下层是web server,上游是各个数据模块。都是基于socket进行数据交互。该业务模块的次要工作模型是:响应web server的申请,依据申请从各个后端数据模块读取相应数据,并依据数据产出最终的HTML页面返回给web服务器。
为了不便后续介绍,定义CUI示意用C实现的模块,PHPUI示意用PHP实现的模块。
4.2.2C/C++模块的性能数据后果
09年,该模块重构抉择了一个新的C/C++框架。过后重构的时候,该模块连贯的后端数据模块规模在5-7个。
基于C/C++的模块,最终测试数据数据分成两个局部:
一、性能比照测试。
基于过后线上压力,进行实在数据的性能测试。所以过后只测试一个压力数据如下:
压力:210QPS
CPU(IDLE):84.18
二、极限性能测试1。
该测试模型是:CUI只连贯一个外围数据模块,其余数据模块齐全敞开。
三、极限性能测试2。
该测试模型是:CUI连贯后端一个外围数据模块,3个数据模块,其余数据模块不连贯。
4.2.3 PHP实现模块的性能测试数据
到11年,基于09年的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交互次数,比如说字符串解决,也比如说网络交互包大小。
OK。那么接下来的要害是找出影响性能的关键因素。
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须要运行在Zend Engine虚拟机上,并且在变量查找、函数调用、作用域切换等各个方面须要一些额定开销。
2、从PHP的基准性能来看,PHP自身不会存在显著的资源耗费,单机QPS可能轻松过W, PHP框架自身也不会对业务零碎的性能带来关键性的影响。
3、从实在的利用场景来看,基于C语言实现的模块不见得比基于PHP实现的模块性能高效很多。因为在实在的利用场景中,更多的性能开销在于网络数据交互和字符串解决。语言方面渺小的性能差别不会成为瓶颈。
据此,能够推出:基于C语言实现的大部分业务零碎都能够思考迁徙到PHP上来,一方面可能疾速开发,另外一方面性能也不会存在问题。
发表回复