精华内容
下载资源
问答
  • php性能优化
    千次阅读
    2018-06-07 15:01:57

    PHP优化对于PHP的优化主要是对php.ini中的相关主要参数进行合理调整和设置,以下我们就来看看php.ini中的一些对性能影响较大的参数应该如何设置。
    # vi /etc/PHP.ini

    (1) php函数禁用找到:

    disable_functions =
    该选项可以设置哪些PHP函数是禁止使用的,PHP中有一些函数的风险性还是相当大的,可以直接执行一些系统级脚本命令,如果允许这些函数执行,当PHP程序出现漏洞时,损失是非常严重的!以下我们给出推荐的禁用函数设置:

    disable_functions = phpinfo,passthru,exec,system,popen,chroot,escapeshellcmd,escapeshellarg,shell_exec,proc_open,proc_get_status

    需注意:如果您的服务器中含有一些系统状态检测的PHP程序,则不要禁用shell_exec,proc_open,proc_get_status等函数。

    (2) PHP脚本执行时间找到:

    max_execution_time = 30

    该选项设定PHP程序的最大执行时间,如果一个PHP脚本被请求,且该PHP脚本在max_execution_time时间内没能执行完毕,则PHP不再继续执行,直接给客户端返回超时错误。没有特殊需要该选项可保持默认设置30秒,如果您的PHP脚本确实需要长执行时间则可以适当增大该时间设置。

    (3) PHP脚本处理内存占用找到:

    memory_limit = 8M

    该选项指定PHP脚本处理所能占用的最大内存,默认为8MB,如果您的服务器内存为1GB以上,则该选项可以设置为12MB以获得更快的PHP脚本处理效率。

    (4) PHP全局函数声明找到:

    register_globals = Off

    网络上很多关于PHP设置的文章都推荐将该选项设置为On,其实这是一种及其危险的设置方法,很可能引起严重的安全性问题。如果没有特殊的需要,强烈推荐保留默认设置!

    (5) PHP上传文件大小限制找到:

    upload_max_filesize = 2M

    该选项设定PHP所能允许最大上传文件大小,默认为2MB。根据实际应用需求,可以适当增大该设置。

    (6) Session存储介质找到:

    session.save_path

    如果你的PHP程序使用Session对话,则可以将Session存储位置设置为/dev/shm,/dev/shm是Linux系统独有的TMPFS文件系统,是以内存为主要存储方式的文件系统,比RAMDISK更优秀,因为可以使用DISKSWAP作为补充,而且是系统自带的功能模块,不需要另行配置。想想看,从磁盘IO操作到内存操作,速度会快多少?只是需要注意,存储在/dev/shm的数据,在服务器重启后会全部丢失。不过这对于Session来说是无足轻重的。

    原文地址

    更多相关内容
  • 1.将PHP升级到最新版 提高性能的最简单的方式是不断升级、更新PHP版本。 2.使用分析器 网站运行缓慢的原因颇多,Web应用程序极其复杂,让人扑朔迷离。而一种可能性在于PHP代码本身。这个分析器可以帮助你快速找出...
  • Benchmark是PEAR的一个扩展包,提供Timer、Iterate和Profiler三个工具类,可用于性能优化过程中的断点调试获取代码的执行时间。如何安装PEAR打开源码安装的PHP根目录,如C:\Program Files\php5\php-5.2.6\,双击go-...
  • PHP性能优化方式

    2018-05-13 16:34:39
    PHP性能优化,从PHP代码本身来优化程序的执行瓶颈,可以解决工作中大部分PHP方面的性能问题
  • 这是PHP性能优化系列第二期,如何使用PEAR工具类Benchmark逐行获取代码或函数的执行时间。工欲善其事,必先利其器!如何安装PEAR和Benchmark请参考PHP性能优化系列第一期 [PHP性能优化准备篇图解PEAR安装]Benchmark...
  • php语言级的性能优化—>php周边问题的性能优化(连接的服务,网络环境)—>php语言自身分析和优化 (php语言级) 优化点:少写代码,多用php自身能力 问题:自写代码冗余较多,可读性不佳,导致性能低 为什么低:...
  • 主要介绍了PHP性能优化大全(php.ini),需要的朋友可以参考下
  • 本篇文章是对PHP性能优化进行了详细的分析介绍,需要的朋友参考下
  • PHP性能优化

    2013-04-26 15:39:23
    PHP性能优化,运维必备。PHP性能优化
  • 关于 PHP 性能优化

    万次阅读 2016-12-15 09:23:17
    本文写的也是关于PHP性能优化方面的话题,虽然老生常谈,但还是以我的角度来一个总结或分享。 网上关于50条PHP优化的方法,除此之外从架构或环境方面的优化建议等,是非常有益的。下面讲讲我所关注的一些方法或建议...
            本文写的也是关于PHP性能优化、减少耗时方面的话题,虽然老生常谈,但还是以我的角度来一个总结或分享。

            网上关于50条PHP优化的方法,除此之外从架构或环境方面的优化建议等,是非常有益的。

           本文讲讲我所关注的一些方法或建议。

            一般来说,性能优化可先从大的方向开始考虑,从对影响性能比较大的因素来考虑,比如现在使用PHP5.7,性能据说可以成倍提高,最后考虑的应该是PHP语法细节上。

    1. PHP部署环境
    单台服务器常用apache+php和nginx+php-fpm方式部署,我们一直使用apache+php方式,据说现在用nginx+php-fpm部署方式性能比apache+php性能好,可考虑一试。另外就是像nginx+swoole等,也是可选项。
    集群是在此基础上,使用nginx/lvs/云上lbs等反向代理作为负载均衡前端。PHP集群部署在可靠性的基础上,PHP集群处理性能比单台服务器有N倍提高(但作为服务的整体性能并不一定有N倍提升)。所以简单地可以认为,通过集群扩展服务器,可以使PHP服务性能得于提升。
    不过,我们更关心的是单台PHP服务器性能优化提升,如何极尽所能。优化包括PHP所处的服务器、运行环境及其自身,但服务器内核配置、运行环境nginx/apache优化,本文不涉及。

    2.PHP扩展使用
    PHP扩展除了使用方便,还是提升性能的亲密伙伴。主要应用有三点:
    1). 开启opcode的缓存,来避免重复的编译。可以使用APC,eAccelerator,XCache等PHP扩展,我们使用xcache。这种只要安装即可的事,为什么不用?
    2). 使用扩展提供的方法(或PHP标准库的方法),扩展实现的效率比PHP代码中的高。但实际上满足我们项目的扩展方法有限,很多基础方法需要时一步封装,除非有能力自己开发扩展。可考虑使用扩展实现的PHP框架,如phalcon、yaf。
    3). 本地缓存,也常用扩展来支持,比如xcache。本地可使用缓存扩展,缓存一些配置数据、元数据或主数据,不用每次都从数据库或文件中读取。
    另,PHP版本上,现在可以考虑升到PHP7……

    3. 文件加载和操作
    这是非常重要的优化建议,尽量减少文件的读写,文件操作包括:文件读、判断文件是否存在、判断文件大小,特别是对于磁盘,减少文件操作即减少寻道时间,读取时间。减少一个文件操作,比优化N个CPU指令(request/request_once,echo/print,单引还是双引)、内存的性能效果要好得多。
    实际应用中,关注以下几个地方:
    1). 把.htaccess的内容写到apache配置中。平时我们都是通过.htaccess作为文件放置到PHP项目的根目录中,作为URL重写配置等。这就造成每次HTTP请求,都要先读取.htaccess,多了一次文件操作。通常.htaccess文件内容也不需要修改,因此可考虑在apache中配置,并禁用.htaccess文件。
    2). PHP程序中,减少file_exists等文件操作函数的使用。在路由框架中,判断要引用的文件是否存在,如果不存在则显示错误,存在则执行文件里的类方法,如:
    if (file_exists($invoke ['path'])) {
         request $invoke ['path'];
         //执行类方法
    }else{
        //显示错误
    }

    为什么不直接request $invoke ['path']呢?其实我们访问的路径(文件),正常的话都是存在的,所以没必要使用file_exists判断是否存在。但是访问到不存在的文件怎么呢?用set_error_handler方式全局处理。但是我只想对当前引用文件错误做特殊处理,把错误处理留在自己的框架中,不使用用户的全局的错误处理呢?可以这样 :
    $hd = set_error_handler(array($this, "page_not_found"));
    require $invoke ['path'];
    if($hd){
    	set_error_handler($hd);
    }

    在require前,重新设置一个错误处理方法A,并会返回之前设置的错误处理方法;当require文件不存在时,会执行A,require正常时,重新把用户的error_handler设置回去。

    还有一个就是日志的处理,可能每次操作日志都要判断一下日志是否存在(不存在即创建)和获取日志大小(分隔日志文件),一般来说,日志文件存在的可能性大,所以直接获取文件大小即可,通过获取日志文件大小一个方法filesize,同时可判断文件是否存在,不存在创建文件即可。这就减少一个文件操作方法的使用。如何使用filesize即可获取大小,又可判断文件是否存在,又不离开当前流程,正常执行下去呢?大家可以思考一下,因为filesize一个不存在的文件是会报错误的哦。
    总之,只要是文件存在的可能性或者命中率高,就可考虑不要使用file_exists。


    3). 类的加载中,使用精确加载并缓存,不要遍历目录文件的方式。如果在一个请求中,只加载且只加载一次需要的类文件是最好的。

    4). 将文件缓存转成内存缓存。


    4. 框架的选取

    除了上面说的PHP扩展框架,还有很多其它非扩展实现的PHP框架,像thinkphp,laravel,这些框架是通用的,封装好、功能全,但自身会损耗一定的性能,主要原因我认为是加载的文件太多、定义的方法变量、检测的东西太多、执行的流程太长。但既然用了,这个得接受,能做的是在语法细节、代码逻辑上进行优化。仅就代码执行(没有外部调用,数据库连接等),框架的执行时间远远大于应用代码执行时间,如果你还用上smarty模块引擎,性能必大打折扣。但是除了性能上考虑,还有很多其它因素值得利用这些成熟框架的。如果觉得性能是要考虑的方面,可以选择轻量一点、偶合性小的框架、或只选取需要的组件。


    5.功能组件的使用

         组合使用,需要对整个框架有一个认识和把控。

    1). 如果可以选用组件,第一个选择的就是路由组件了,能方便地路由到指定控制器方法即可,不要做多余的事,少用正则匹配,约定优于配置。

    2). 类加载功能。上面说的,按需精确加载并缓存,一次请求有且只require一次。我们项目类的加载很原始,require_once and new。

    3). 参数校验和安全处理。实际上这个也是相对耗费性能的过程,但为了安全还是有必要的。能做到按需参数处理最好,但是有时为方便,还是会在入口处全局处理安全过滤,参数校验就可以在控制器方法中处理。

    4). session,简单地通过配置保存到redis/memcache等缓存中,或者存到cookie中。但是根据默认的机制,session初始化(读取)和请求结束(写回),会产生两次网络操作。根据我们应用场景分析,session内容是不常变的,在不变的情况下可考虑只读而不写回(因此也就有不能更新session修改时间 的问题),这就要自己实现PHP会话的接口了,只有当有session修改时,变更的时候才回写session存储,这样,少一个网络操作可省多少时间啊。有些实现得不好的,在一个请求中多次连接或多次操作存储的,就不可取了。另外,结合cookieSession,即把session加密存到cookie中,对session的处理也会减少很多耗时。

    5). 视图模板引擎。如果是API类接口服务,直接返回数据,如果是网页,那就需要使用模板引擎了。模板引擎也是很耗费性能的主,当然,喜欢原生态的方式,那是最高效的,像smarty这种,大而全,性能低效,而且很多功能我们都用不上。我们使用模板引擎,最核心的是html与PHP分离,然后才是变量与语法的处理。所以,要享受模板引擎的分离好处,又追求性能,那可以参考tmd_tpl。只实现分离,语法上使用PHP语法,因为如果要自己实现一套语法,需要大量的查找和替换。同样在模板引擎中,可以进一步合并多个视图文件为一个,减少文件操作等方式来优化。

    6). 数据库方面。使用PDO,ORM使用方便但也有一定耗时,数组作为数据对象最高效,一般用短连接,使用单例连接对象或连接池(有些扩展可支持)。


    待补充

    展开全文
  • LNMP 性能优化PHP 性能优化

    千次阅读 2017-10-03 11:19:52
    PHP 性能优化初探 使用 PHP 其语法不恰当使用 PHP 做其不擅长的事使用 PHP 连接的服务不稳定使用 PHP 但尚未排查出来的问题 PHP 性能问题简析 PHP 运行流程PHP 开销和速度排序 PHP 语言级性能优化 尽可能...

    转载自:https://github.com/luisedware/Archives/issues/4

    目录

    PHP 性能优化初探

    使用 PHP 其语法不恰当

    PHP 做一门弱类型动态语言,在上手容易和开发快速同时,也会导致一些新手写出不规范的代码。比如在递归当中连接数据库读取数据;一次性从文件中读取大量的数据,处理完后却不主动释放内存;在遍历和循环中重复计算某个变量等等;数组的键没有加引号导致先查找常量集,都会导致 PHP 程序性能下降。

    使用 PHP 做其不擅长的事

    PHP 作为一门 Web 后端脚本语言,好处是能够快速实现 Web Application 所需功能,而且容易部署。缺点就是相对于强类型静态语言如 Java/C/C++ 来说,PHP 的性能较差,在实现计算密集型的业务时没有任何优势。同时也由于 PHP 是同步阻塞的 IO 模型,在高并发请求的场景下容易遇到瓶颈,需要通过 PHP 相关扩展来解决相关技术难题。

    使用 PHP 连接的服务不稳定

    PHP 作为一门胶水语言,势必会连接各种各样服务。常见的服务如:MySQL、Redis、MongoDB 等数据库,C/C++、GO、Java 等语言编写的后端服务。倘若 PHP 所连接服务不稳定,势必也会对 PHP 造成一定的性能影响。

    使用 PHP 但尚未排查出来的问题

    在某些情况,某个 PHP 程序或某段 PHP 代码莫名其妙地出现相当耗时的情况,不知道是 PHP 本身出现了问题,还是所用的框架出现了问题,亦或是 PHP 周边甚至是硬件的问题。这个时候就需要通过工具进行排查。常用的工具有:PHP-Xhprof、PHP-XDebug。

    PHP 性能问题简析

    PHP 的底层是由 C 语言组成的。每次运行 PHP 的程序,都是需要经过 C 语言遍写的 Zend 引擎来解析 PHP 文件,将其编译成为 Opcodes 后再去执行。就这样一来一回就消耗了不少时间和硬件性能。

    PHP 运行流程

    1. Scanning(Lexing),将 PHP 代码转换为语言片段(Tokens)。
    2. Parsing,将 Tokens 转换成简单而有意义的表达式(Expression)。
    3. Compilation,将表达式编译成 Opocdes。
    4. Execution,顺次执行 Opcodes,每次一条,从而实现 PHP 脚本的功能。

    (*.php) -> scanner -> (Tokens) -> Parser -> (Expression) -> Compilation -> (Opcodes) -> Execution -> (Output)

    PHP 开销和速度排序

    在日常使用中,PHP 各项性能开销和运行速度如下:

    • 性能开销(从大到小)
      • 硬盘 I/O
      • 数据库 I/O
      • 内存 I/O
      • 网络 I/O
    • 运行速度(从快到慢)
      • 内存 I/O
      • 数据库 I/O
      • 硬盘 I/O
      • 网络 I/O

    读写网络数据本质也是硬盘操作,而且网络都是有延迟的,这也带了隐性的时间浪费,事实上 Web Application 感觉运行速度低下的原因,一般也都是网络 I/O 和 硬盘 I/O 引起的问题。

    PHP 语言级性能优化

    尽可能地使用内置函数来完成任务

    能使用 PHP 内置方法解决的问题,就不要自己手写代码,一是手写代码一般冗余较多,可读性不佳。二是手写代码需要解析编译为底层代码再执行,没有 PHP 内置函数的性能高。

    for & range() 实现同一功能

    <?php
    
    for ($i = 0; $i <1000; $i++) {
    	$array1[$i] = $i+1000;
    }
    
    range(1000,1999);

    以 foreach、in_array 和 array_merge 实现同一功能对比说明:

    <?php
    
    arrayMerged = [];
    foreach ($array1 as $value) {
    	$arrayMerged[] = $value;
    }
    foreach ($array2 as $value) {
    	if(!in_array($value, $arrayMerged)){
    		$arrayMerged[] = $value;
    	}
    }
    
    array_merge($array1,$array2);

    以 foreach 和 array_column() 实现同一功能对比说明:

    <?php
    
    $usernames = [];
    foreach ($array as $key => $value) {
    	if(isset($value['username']) && !empty($value['username'])){
    		$usernames[$value['id']] = $value;
    	}
    }
    
    array_column($array, 'username','id');

    以 foreach 和 array_filter() 实现同一功能对比说明:

    <?php
    
    $arr = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4];
    foreach ($arr as $key => $value) {
    	if($key === 'b'){
    		$result[$key] = $value;
    	}
    	if($value === 4){
    		$result[$key] = $value;
    	}
    }
    
    array_filter($arr, function($v, $k) {
    	return $k == 'b' || $v == 4;
    }, ARRAY_FILTER_USE_BOTH)

    尽可能地使用高性能的内置函数来完成任务

    在 PHP 内置的函数之间,实现同一个功能时,也会存在着性能的差距。这是因为 PHP 内置函数的时间复杂度各不相同,了解各个 PHP 内置函数的时间复杂度,也有利于从代码层优化 PHP 项目。

    以 isset() 和 array_key_exists() 对比说明:

    <?php
    
    function current_unix_time(){
    	list($usec,$sec) = explode(" ", microtime());
    	return ((float)$usec + (float)$sec);
    }
    
    $time = -current_unix_time();
    $i = 0;
    $array = range(1, 1000000);
    while ($i < 1000000) {
    	if(array_key_exists($i, $array)){
    
    	}
    	$i++;
    }
    $time += current_unix_time();
    echo $time."\n";
    
    $time2 = -current_unix_time();
    $i = 0;
    $array = range(1, 1000000);
    while ($i < 1000000) {
    	if(isset($array[$i])){
    
    	}
    	$i++;
    }
    $time2 += current_unix_time();
    echo $time2;
    
    // array_key_exists:0.080096006393433
    // isset:0.041244029998779

    尽可能避免使用魔法方法来完成任务

    TODO

    尽可能避免使用错误抑制符来完成任务

    TODO

    尽可能避免使用正则表达式来完成任务

    正则表达式在 PHP 里算是一把双刃剑;它的优势就是使用起来简单。它的劣势就是性能低下。因为正则表达式需要回溯,而回溯的性能开销比较大,需要去优化。但优化正则表达式是需要一定的技术水平的。在常见的业务场景中,不妨使用字符串处理函数或 filter_var() 函数来实现功能。

    尽可能避免在遍历或循环内做各种运算

    使用 for、while 循环的时候,循环内的计算式将会被重复计算,这样就造成了一些可避免的不必要性能开销。

    <?php
    
    // 修改前
    for($i = 0; $i < strlen("hello world"); $i++){
    	//do something
    }
    
    // 修改后
    $str = "hello world";
    $strlen = strlen($str);
    for($i = 0; $i < $strlen; $i++){
    	//do something
    }

    尽可能避免在计算密集型的业务中使用

    什么是计算密集型的业务?一般来说是大批量的日志分析、大批量的数据运算。PHP 的语言特性决定了 PHP 不适合做大数据量的运算。因为 PHP 是由 C 语言编写的,
    PHP 的代码最终还是要转换成 C 语言去执行,这就需要一部分的性能开销。再加上 PHP 自身环境和语言特性缘故,PHP 做运算相比于 C 语言来说,不仅性能开销大,运算速度也慢,所以 PHP 不是适合做计算密集型的业务。PHP 的语言特性决定了它比较适合衔接后端服务或渲染页面。

    PHP 周边级性能优化

    Server 服务器

    从服务器方面进行优化,可以选择将服务器不安装其他后端服务软件,仅仅安装 PHP 以及其必要扩展。使单机的性能全部向 PHP 倾斜。同时也对 PHP 的相关参数进行优化,将 PHP 单机服务器性能最大化。在大数据、高并发的场景下,可以尝试将 PHP 服务器集群化,通过负载均衡,将网络请求分配至不同的 PHP 单机服务器处理。

    Network 网络

    从网络方面进行优化时的需要注意的两点:

    • 对接接口是否稳定
    • 连接网络是否稳定

    如何优化网络:

    • 使用更好的网卡:使用千兆以上的网卡
    • 选择更好的套餐:更多的流量,更高的带宽
    • 设置超时时间:
      • 连接超时:200 ms
      • 读取超时:800 ms
      • 写入超时:500 ms
      • 可以根据实际情况进行自由调整
    • 将串行的网络连接并行化
      • 使用 curl_multi_*() 系列函数
      • 使用 Swoole 扩展或 Wokerman
    • 压缩网络请求数据
      • 启用 Server 的 Gzip 相关功能,最好是网络传输的数据大于 100 KB 的时候再使用,否则压缩包比源数据还大就徒劳了。
      • 压缩优势是减少客户端获取数据速度;劣势是由于 Server 需要压缩数据,会产生额外的 CPU 开销;而 Client 需要解压数据,客户端也会产生额外的 CPU 开销。

    Database 数据库

    数据库可以进行优化的层面:

    • 服务器硬件
    • 数据库配置
    • 数据库表结构
    • SQL 语句和索引

    以 MySQL 为例,在服务器硬件方面,可以选择多核的 CPU,现在数据库软件对多核 CPU 都有优化;在数据库配置方面,可以根据监控日志和性能压测数据进行调优;数据表结构方面可以根据需要存储的数据选择最优的数据类型,设计时可以根据数据库表的范式来设计减少冗余数据,也可以反范式设计提高查询性能;如果数据库表过大,可以采取垂直拆分和水平拆分。而 SQL 语句和索引方面,可以通过开启 mysqldumpslow 和 pt-query-digest 来查看慢查询。使用 EXPLAIN 分析 SQL 的执行计划;使用普通索引或复合索引增加查询数据;了解 MySQL 执行的细节写出最优解的 SQL 语句。

    Cache 缓存

    TODO

    File 文件

    TODO

    PHP 瓶颈级性能优化

    Opcode 优化

    • 启用 Zend Opcache,以 PHP 7 为例,在配置文件 php.ini 加入以下代码即可:
    zend_extension=opcache.so
    opcache.enable=1
    opcache.enable_cli=1"
    

    Runtime 优化

    PHP 扩展编写

    PHP 性能分析工具

    ApacheBench 的参数讲解与基础使用

    ApacheBench 参数讲解与基础使用:https://github.com/luisedware/Archives/issues/2

    Vld Opcache 的参数讲解与基础使用

    TODO

    Xhprof of PHP 7 的参数讲解与基础使用

    TODO

    展开全文
  • PHP 性能优化

    千次阅读 2016-08-23 19:52:22
    尽管在PHP 5.2中,记录可能根相对于完全不记录可能根要慢些,而PHP 5.3中对 PHP run-time 的其他修改减少了这个性能损失。 这里主要有两个领域对性能有影响。第一个是内存占用空间的节省,另一个是垃圾回收机制执行...

        php.net官网上给出了两点解释:

       

          回收可能根有细微的性能上影响,但这是把PHP 5.2与PHP 5.3比较时才有的。尽管在PHP 5.2中,记录可能根相对于完全不记录可能根要慢些,而PHP 5.3中对 PHP run-time 的其他修改减少了这个性能损失。

    这里主要有两个领域对性能有影响。第一个是内存占用空间的节省,另一个是垃圾回收机制执行内存清理时的执行时间增加(run-time delay)。我们将研究这两个领域。

    内存占用空间的节省 ¶

    首先,实现垃圾回收机制的整个原因是为了,一旦先决条件满足,通过清理循环引用的变量来节省内存占用。在PHP执行中,一旦根缓冲区满了或者调用gc_collect_cycles() 函数时,就会执行垃圾回收。在下图中,显示了下面脚本分别在PHP 5.2 和 PHP 5.3环境下的内存占用情况,其中排除了脚本启动时PHP本身占用的基本内存。

    Example #1 Memory usage example

    <?php
    class Foo
    {
        public 
    $var '3.1415962654';
    }

    $baseMemory memory_get_usage();

    for ( 
    $i 0$i <= 100000$i++ )
    {
        
    $a = new Foo;
        
    $a->self $a;
        if ( 
    $i 500 === )
        {
            echo 
    sprintf'%8d: '$i ), memory_get_usage() - $baseMemory"\n";
        }
    }
    ?>
    Comparison of memory usage between PHP 5.2 and PHP 5.3

    在这个很理论性的例子中,我们创建了一个对象,这个对象中的一个属性被设置为指回对象本身。在循环的下一个重复(iteration)中,当脚本中的变量被重新复制时,就会发生典型性的内存泄漏。在这个例子中,两个变量容器是泄漏的(对象容器和属性容器),但是仅仅能找到一个可能根:就是被unset的那个变量。在10,000次重复后(也就产生总共10,000个可能根),当根缓冲区满时,就执行垃圾回收机制,并且释放那些关联的可能根的内存。这从PHP 5.3的锯齿型内存占用图中很容易就能看到。每次执行完10,000次重复后,执行垃圾回收,并释放相关的重复使用的引用变量。在这个例子中由于泄漏的数据结构非常简单,所以垃圾回收机制本身不必做太多工作。从这个图表中,你能看到 PHP 5.3的最大内存占用大概是9 Mb,而PHP 5.2的内存占用一直增加。

    执行时间增加(Run-Time Slowdowns) ¶

    垃圾回收影响性能的第二个领域是它释放已泄漏的内存耗费的时间。为了看到这个耗时时多少,我们稍微改变了上面的脚本,有更多次数的重复并且删除了循环中的内存占用计算,第二个脚本代码如下:

    Example #2 GC性能影响

    <?php
    class Foo
    {
        public 
    $var '3.1415962654';
    }

    for ( 
    $i 0$i <= 1000000$i++ )
    {
        
    $a = new Foo;
        
    $a->self $a;
    }

    echo 
    memory_get_peak_usage(), "\n";
    ?>

    我们将运行这个脚本两次,一次通过配置zend.enable_gc 打开垃圾回收机制时,另一次是它关闭时。

    Example #3 执行以上脚本

    time php -dzend.enable_gc=0 -dmemory_limit=-1 -n example2.php
    # and
    time php -dzend.enable_gc=1 -dmemory_limit=-1 -n example2.php

    在我的机器上,第一个命令持续执行时间大概为10.7秒,而第二个命令耗费11.4秒。时间上增加了7%。然而,执行这个脚本时内存占用的峰值降低了98%,从931Mb 降到 10Mb。这个基准不是很科学,或者并不能代表真实应用程序的数据,但是它的确显示了垃圾回收机制在内存占用方面的好处。好消息就是,对这个脚本而言,在执行中出现更多的循环引用变量时,内存节省的更多的情况下,每次时间增加的百分比都是7%。

    PHP内部 GC 统计信息 ¶

    在PHP内部,可以显示更多的关于垃圾回收机制如何运行的信息。但是要显示这些信息,你需要先重新编译PHP使benchmark和data-collecting code可用。你需要在按照你的意愿运行./configure前,把环境变量CFLAGS设置成-DGC_BENCH=1。下面的命令串就是做这个事:

    Example #4 重新编译PHP以启用GC benchmarking

    export CFLAGS=-DGC_BENCH=1
    ./config.nice
    make clean
    make

    当你用新编译的PHP二进制文件来重新执行上面的例子代码,在PHP执行结束后,你将看到下面的信息:

    Example #5 GC 统计数据

    GC Statistics
    -------------
    Runs:               110
    Collected:          2072204
    Root buffer length: 0
    Root buffer peak:   10000
    
          Possible            Remove from  Marked
            Root    Buffered     buffer     grey
          --------  --------  -----------  ------
    ZVAL   7175487   1491291    1241690   3611871
    ZOBJ  28506264   1527980     677581   1025731

    主要的信息统计在第一个块。你能看到垃圾回收机制运行了110次,而且在这110次运行中,总共有超过两百万的内存分配被释放。只要垃圾回收机制运行了至少一次,根缓冲区峰值(Root buffer peak)总是10000.

    结论 ¶

    通常,PHP中的垃圾回收机制,仅仅在循环回收算法确实运行时会有时间消耗上的增加。但是在平常的(更小的)脚本中应根本就没有性能影响。

    然而,在平常脚本中有循环回收机制运行的情况下,内存的节省将允许更多这种脚本同时运行在你的服务器上。因为总共使用的内存没达到上限。

    这种好处在长时间运行脚本中尤其明显,诸如长时间的测试套件或者daemon脚本此类。同时,对通常比Web脚本运行时间长的» PHP-GTK应用程序,新的垃圾回收机制,应该会大大改变一直以来认为内存泄漏问题难以解决的看法。


    接下来,看了一篇文章写的挺棒的,拿facebook举例,供大家参考,小弟确实学艺不深

    HHVM 是如何提升 PHP 性能的?

    背景

    HHVM 是 Facebook 开发的高性能 PHP 虚拟机,宣称比官方的快9倍,我很好奇,于是抽空简单了解了一下,并整理出这篇文章,希望能回答清楚两方面的问题:

    • HHVM 到底靠谱么?是否可以用到产品中?
    • 它为什么比官方的 PHP 快很多?到底是如何优化的?

    你会怎么做?

    在讨论 HHVM 实现原理前,我们先设身处地想想:假设你有个 PHP 写的网站遇到了性能问题,经分析后发现很大一部分资源就耗在 PHP 上,这时你会怎么优化 PHP 性能?

    比如可以有以下几种方式:

    • 方案1,迁移到性能更好的语言上,如 Java、C++、Go。
    • 方案2,通过 RPC 将功能分离出来用其它语言实现,让 PHP 做更少的事情,比如 Twitter 就将大量业务逻辑放到了 Scala 中,前端的 Rails 只负责展现。
    • 方案3,写 PHP 扩展,在性能瓶颈地方换 C/C++。
    • 方案4,优化 PHP 的性能。

    方案1几乎不可行,十年前 Joel 就拿 Netscape 的例子警告过,你将放弃是多年的经验积累,尤其是像 Facebook 这种业务逻辑复杂的产品,PHP 代码实在太多了,据称有2千万行(引用自 [PHP on the Metal with HHVM]),修改起来的成本恐怕比写个虚拟机还大,而且对于一个上千人的团队,从头开始学习也是不可接受的。

    方案2是最保险的方案,可以逐步迁移,事实上 Facebook 也在朝这方面努力了,而且还开发了 Thrift 这样的 RPC 解决方案,Facebook 内部主要使用的另一个语言是 C++,从早期的 Thrift 代码就能看出来,因为其它语言的实现都很简陋,没法在生产环境下使用。

    目前在 Facebook 中据称 PHP:C++ 已经从 9:1 增加到 7:3 了,加上有 Andrei Alexandrescu 的存在,C++ 在 Facebook 中越来越流行,但这只能解决部分问题,毕竟 C++ 开发成本比 PHP 高得多,不适合用在经常修改的地方,而且太多 RPC 的调用也会严重影响性能。

    方案3看起来美好,实际执行起来却很难,一般来说性能瓶颈并不会很显著,大多是不断累加的结果,加上 PHP 扩展开发成本高,这种方案一般只用在公共且变化不大的基础库上,所以这种方案解决不了多少问题。

    可以看到,前面3个方案并不能很好地解决问题,所以 Facebook 其实没有选择的余地,只能去考虑 PHP 本身的优化了。

    更快的 PHP

    既然要优化 PHP,那如何去优化呢?在我看来可以有以下几种方法:

    • 方案1,PHP 语言层面的优化。
    • 方案2,优化 PHP 的官方实现(也就是 Zend)。
    • 方案3,将 PHP 编译成其它语言的 bytecode(字节码),借助其它语言的虚拟机(如 JVM)来运行。
    • 方案4,将 PHP 转成 C/C++,然后编译成本地代码。
    • 方案5,开发更快的 PHP 虚拟机。

    PHP 语言层面的优化是最简单可行的,Facebook 当然想到了,而且还开发了 XHProf 这样的性能分析工具,对于定位性能瓶颈是很有帮助的。

    不过 XHProf 还是没能很好解决 Facebook 的问题,所以我们继续看,接下来是方案2,简单来看,Zend 的执行过程可以分为两部分:将 PHP 编译为 opcode、执行 opcode,所以优化 Zend 可以从这两方面来考虑。

    优化 opcode 是一种常见的做法,可以避免重复解析 PHP,而且还能做一些静态的编译优化,比如 Zend Optimizer Plus,但由于 PHP 语言的动态性,这种优化方法是有局限性的,乐观估计也只能提升20%的性能。另一种考虑是优化 opcode 架构本身,如基于寄存器的方式,但这种做法修改起来工作量太大,性能提升也不会特别明显(可能30%?),所以投入产出比不高。

    另一个方法是优化 opcode 的执行,首先简单提一下 Zend 是如何执行的,Zend 的 interpreter(也叫解释器)在读到 opcode 后,会根据不同的 opcode 调用不同函数(其实有些是 switch,不过为了描述方便我简化了),然后在这个函数中执行各种语言相关的操作(感兴趣的话可看看深入理解 PHP 内核这本书),所以 Zend 中并没有什么复杂封装和间接调用,作为一个解释器来说已经做得很好了。

    想要提升 Zend 的执行性能,就需要对程序的底层执行有所解,比如函数调用其实是有开销的,所以能通过 Inline threading 来优化掉,它的原理就像 C 语言中的 inline 关键字那样,但它是在运行时将相关的函数展开,然后依次执行(只是打个比方,实际实现不太一样),同时还避免了 CPU 流水线预测失败导致的浪费。

    另外还可以像 JavaScriptCore 和 LuaJIT 那样使用汇编来实现 interpreter,具体细节建议看看 Mike 的解释

    但这两种做法修改代价太大,甚至比重写一个还难,尤其是要保证向下兼容,后面提到 PHP 的特点时你就知道了。

    开发一个高性能的虚拟机不是件简单的事情,JVM 花了10多年才达到现在的性能,那是否能直接利用这些高性能的虚拟机来优化 PHP 的性能呢?这就是方案3的思路。

    其实这种方案早就有人尝试过了,比如 Quercus 和 IBM 的 P8,Quercus 几乎没见有人使用,而 P8 也已经死掉了。Facebook 也曾经调研过这种方式,甚至还出现过不靠谱的传闻 ,但其实 Facebook 在2011年就放弃了。

    因为方案3看起来美好,但实际效果却不理想,按照很多大牛的说法(比如 Mike),VM 总是为某个语言优化的,其它语言在上面实现会遇到很多瓶颈,比如动态的方法调用,关于这点在 Dart 的文档中有过介绍,而且据说 Quercus 的性能与 Zend+APC 比差不了太多([来自The HipHop Compiler for PHP]),所以没太大意义。

    不过 OpenJDK 这几年也在努力,最近的 Grall 项目看起来还不错,也有语言在上面取得了显著的效果,但我还没空研究 Grall,所以这里无法判断。

    接下来是方案4,它正是 HPHPc(HHVM 的前身)的做法,原理是将 PHP 代码转成 C++,然后编译为本地文件,可以认为是一种 AOT(ahead of time)的方式,关于其中代码转换的技术细节可以参考 The HipHop Compiler for PHP 这篇论文,以下是该论文中的一个截图,可以通过它来大概了解:

    这种做法的最大优点是实现简单(相对于一个 VM 来说),而且能做很多编译优化(因为是离线的,慢点也没事),比如上面的例子就将- 1优化掉了,但它很难支持 PHP 中的很多动态的方法,如 eval()create_function(),因为这就得再内嵌一个 interpreter,成本不小,所以 HPHPc 干脆就直接不支持这些语法。

    除了 HPHPc,还有两个类似的项目,一个是 Roadsend,另一个是 phc ,phc 的做法是将 PHP 转成了 C 再编译,以下是它将 file_get_contents($f) 转成 C 代码的例子:

    static php_fcall_info fgc_info;
    php_fcall_info_init ("file_get_contents", &fgc_info);
    php_hash_find (LOCAL_ST, "f", 5863275, &fgc_info.params);
    php_call_function (&fgc_info);
    

    话说 phc 作者曾经在博客上哭诉,说他两年前就去 Facebook 演示过 phc 了,还和那里的工程师交流过,结果人家一发布就火了,而自己忙活了4年却默默无闻,现在前途渺茫。。。

    Roadsend 也已经不维护了,对于 PHP 这样的动态语言来说,这种做法有很多的局限性,由于无法动态 include,Facebook 将所有文件都编译到了一起,上线时的文件部署居然达到了 1G,越来越不可接受了。

    另外有还有一个叫 PHP QB 的项目,由于时间关系我没有看,感觉可能是类似的东东。

    所以就只剩下一条路了,那就是写一个更快的 PHP 虚拟机,将一条黑路走到底,或许你和我一样,一开始听到 Facebook 要做一个虚拟机是觉得太离谱,但如果仔细分析就会发现其实也只有这样了。

    更快的虚拟机

    HHVM 为什么更快?在各种新闻报道中都提到了 JIT 这个关键技术,但其实远没有那么简单,JIT 不是什么神奇的魔法棒,用它轻轻一挥就能提升性能,而且 JIT 这个操作本身也是会耗时的,对于简单的程序没准还比 interpreter 慢,最极端的例子是 LuaJIT 2 的 Interpreter 就稍微比 V8 的 JIT 快,所以并不存在绝对的事情,更多还是在细节问题的处理上,HHVM 的发展历史就是不断优化的历史,你可以从下图看到它是如何一点点超过 HPHPc 的:

    值得一提的是在 Android 4.4 中新的虚拟机 ART 就采用的是 AOT 方案(还记得么?前面提到的 HPHPc 就是这种),结果比之前使用 JIT 的 Dalvik 快了一倍,所以说 JIT 也不一定比 AOT 快。

    因此这个项目是有很大风险的,如果没有强大的内心和毅力,极有可能半途而废,Google 就曾经想用 JIT 提升 Python 的性能,但最终失败了,对于 Google 来说用到 Python 的地方其实并没什么性能问题(好吧,以前 Google 是用 Python 写过 crawl [参考 In The Plex],但那都是1996年的事情了)。

    比起 Google,Facebook 显然有更大的动力和决心,PHP 是 Facebook 最重要的语言,我们来看看 Facebook 都投入了哪些大牛到这个项目中(不全):

    • Andrei Alexandrescu,『Modern C++ Design』和『C++ Coding Standards』的作者,C++ 领域无可争议的大神
    • Keith Adams,负责过 VMware 核心架构,当年 VMware 就派他一人去和 Intel 进行技术合作,足以证明在 VMM 领域他有多了解了
    • Drew Paroski,在微软参与过 .NET 虚拟机开发,改进了其中的 JIT
    • Jason Evans,开发了 jemalloc,减少了 Firefox 一半的内存消耗
    • Sara Golemon,『Extending and Embedding PHP』的作者,PHP 内核专家,这本书估计所有 PHP 高手都看过吧,或许你不知道其实她是女的

    虽然没有像 Lars Bak、Mike Pall 这样在虚拟机领域的顶级专家,但如果这些大牛能齐心协力,写个虚拟机还是问题不大的,那么他们将面临什么样的挑战呢?接下来我们一一讨论。

    规范是什么?

    自己写 PHP 虚拟机要面临的第一个问题就是 PHP 没有语言规范,很多版本间的语法还会不兼容(甚至是小版本号,比如 5.2.1 和 5.2.3),PHP 语言规范究竟如何定义呢?来看一篇来自 IEEE 的说法:

    The PHP group claim that they have the final say in the specification of (the language) PHP. This groups specification is an implementation, and there is no prose specification or agreed validation suite.

    所以唯一的途径就是老老实实去看 Zend 的实现,好在 HPHPc 中已经痛苦过一次了,所以 HHVM 能直接利用现成,因此这个问题并不算太大。

    语言还是扩展?

    实现 PHP 语言不仅仅只是实现一个虚拟机那么简单,PHP 语言本身还包括了各种扩展,这些扩展和语言是一体的,Zend 不辞辛劳地实现了各种你可能会用到的功能。如果分析过 PHP 的代码,就会发现它的 C 代码除去空行注释后居然还有80+万行,而你猜其中 Zend 引擎部分有多少?只有不到10万行。

    对于开发者来说这不是什么坏事,但对于引擎实现者来说就很悲剧了,我们可以拿 Java 来进行对比,写个 Java 的虚拟机只需实现字节码解释及一些基础的 JNI 调用,Java 绝大部分内置库都是用 Java 实现的,所以如果不考虑性能优化,单从工作量看,实现 PHP 虚拟机比 JVM 要难得多,比如就有人用8千行的 TypeScript 实现了一个 JVM Doppio

    而对于这个问题,HHVM 的解决办法很简单,那就是只实现 Facebook 中用到的,而且同样可以先用 HPHPc 中之前写过的,所以问题也不大。

    实现 Interpreter

    接下来是 Interpreter 的实现,在解析完 PHP 后会生成 HHVM 自己设计的一种 Bytecode,存储在~/.hhvm.hhbc(SQLite 文件) 中以便重用,在执行 Bytecode 时和 Zend 类似,也是将不同的字节码放到不同的函数中去实现(这种方式在虚拟机中有个专门的称呼:Subroutine threading

    Interpreter 的主体实现在 bytecode.cpp 中,比如 VMExecutionContext::iopAdd 这样的方法,最终执行会根据不同类型来区分,比如 add 操作的实现是在 tv-arith.cpp 中,下面摘抄其中的一小段

    if (c2.m_type == KindOfInt64)  return o(c1.m_data.num, c2.m_data.num);
    if (c2.m_type == KindOfDouble) return o(c1.m_data.num, c2.m_data.dbl);

    正是因为有了 Interpreter,HHVM 在对于 PHP 语法的支持上比 HPHPc 有明显改进,理论上做到完全兼容官方 PHP,但仅这么做在性能并不会比 Zend 好多少,由于无法确定变量类型,所以需要加上类似上面的条件判断语句,但这样的代码不利于现代 CPU 的执行优化,另一个问题是数据都是 boxed 的,每次读取都需要通过类似 m_data.num 和m_data.dbl 的方法来间接获取。

    对于这样的问题,就得靠 JIT 来优化了。

    实现 JIT 及优化

    首先值得一提的是 PHP 的 JIT 之前并非没人尝试过:

    那么究竟什么是 JIT?如何实现一个 JIT?

    在动态语言中基本上都会有个 eval 方法,可以传给它一段字符串来执行,JIT 做的就是类似的事情,只不过它要拼接不是字符串,而是不同平台下的机器码,然后进行执行,但如何用 C 来实现呢?可以参考 Eli 写的这个入门例子,以下是文中的一段代码:

    unsigned char code[] = {
      0x48, 0x89, 0xf8,                   // mov %rdi, %rax
      0x48, 0x83, 0xc0, 0x04,             // add $4, %rax
      0xc3                                // ret
    };
    memcpy(m, code, sizeof(code));
    

    然而手工编写机器码很容易出错,所以最好的有一个辅助的库,比如的 Mozilla 的 Nanojit 以及 LuaJIT 的 DynASM,但 HHVM 并没有使用这些,而是自己实现了一个只支持 x64 的(另外还在尝试用 VIXL 来生成 ARM 64 位的),通过 mprotect 的方式来让代码可执行。

    但为什么 JIT 代码会更快?你可以想想其实用 C++ 编写的代码最终编译出来也是机器码,如果只是将同样的代码手动转成了机器码,那和 GCC 生成出来的有什么区别呢?虽然前面我们提到了一些针对 CPU 实现原理来优化的技巧,但在 JIT 中更重要的优化是根据类型来生成特定的指令,从而大幅减少指令数和条件判断,下面这张来自 TraceMonkey 的图对此进行了很直观的对比,后面我们将看到 HHVM 中的具体例子:

    HHVM 首先通过 interpeter 来执行,那它会在时候使用 JIT 呢?常见的 JIT 触发条件有 2 种:

    • trace:记录循环执行次数,如果超过一定数量就对这段代码进行 JIT
    • method:记录函数执行次数,如果超过一定数量就对整个函数进行 JIT,甚至直接 inline

    关于这两种方法哪种更好在 Lambada 上有个帖子引来了各路大神的讨论,尤其是 Mike Pall(LuaJIT 作者) 、Andreas Gal(Mozilla VP) 和 Brendan Eich(Mozilla CTO)都发表了很多自己的观点,推荐大家围观,我这里就不献丑了。

    它们之间的区别不仅仅是编译范围,还有很多细节问题,比如对局部变量的处理,在这里就不展开了

    但 HHVM 并没有采用这两种方式,而是自创了一个叫 tracelet 的做法,它是根据类型来划分的,看下面这张图

    可以看到它将一个函数划分为了 3 部分,上面 2 部分是用于处理 $k 为整数或字符串两种不同情况的,下面的部分是返回值,所以看起来它主要是根据类型的变化情况来划分 JIT 区域的,具体是如何分析和拆解 Tracelet 的细节可以查看Translator.cpp 中的 Translator::analyze 方法,我还没空看,这里就不讨论了。

    当然,要实现高性能的 JIT 还需进行各种尝试和优化,比如最初 HHVM 新增的 tracelet 会放到前面,也就是将上图的 A 和 C 调换位置,后来尝试了一下放到后面,结果性能提示了 14%,因为测试发现这样更容易提前命中响应的类型

    JIT 的执行过程是首先将 HHBC 转成 SSA (hhbc-translator.cpp),然后对 SSA 上做优化(比如 Copy propagation),再生成本地机器码,比如在 X64 下是由 translator-x64.cpp 实现的。

    我们用一个简单的例子来看看 HHVM 最终生成的机器码是怎样的,比如下面这个 PHP 函数:

    <?php
    function a($b){
      echo $b + 2;
    }
    

    编译后是这个样子:

    mov rcx,0x7200000
    mov rdi,rbp
    mov rsi,rbx
    mov rdx,0x20
    call 0x2651dfb <HPHP::Transl::traceCallback(HPHP::ActRec*, HPHP::TypedValue*, long, void*)>
    cmp BYTE PTR [rbp-0x8],0xa
    jne 0xae00306
    ; 前面是检查参数是否有效
    
    mov rcx,QWORD PTR [rbp-0x10]           ; 这里将 %rcx 被赋值为1了
    mov edi,0x2                            ; 将 %edi(也就是 %rdi 的低32位)赋值为2
    add rdi,rcx                            ; 加上 %rcx
    call 0x2131f1b <HPHP::print_int(long)> ; 调用 print_int 函数,这时第一个参数 %rdi 的值已经是3了
    
    ; 后面暂不讨论
    mov BYTE PTR [rbp+0x28],0x8
    lea rbx,[rbp+0x20]
    test BYTE PTR [r12],0xff
    jne 0xae0032a
    push QWORD PTR [rbp+0x8]
    mov rbp,QWORD PTR [rbp+0x0]
    mov rdi,rbp
    mov rsi,rbx
    mov rdx,QWORD PTR [rsp]
    call 0x236b70e <HPHP::JIT::traceRet(HPHP::ActRec*, HPHP::TypedValue*, void*)>
    ret 
    

    而 HPHP::print_int 函数的实现是这样的:

    void print_int(int64_t i) {
      char buf[256];
      snprintf(buf, 256, "%" PRId64, i);
      echo(buf);
      TRACE(1, "t-x64 output(int): %" PRId64 "\n", i);
    }
    

    可以看到 HHVM 编译出来的代码直接使用了 int64_t,避免了 interpreter 中需要判断参数和间接取数据的问题,从而明显提升了性能,最终甚至做到了和 C 编译出来的代码区别不大。

    需要注意:HHVM 在 server mode 下,只有超过12个请求就才会触发 JIT,启动过 HHVM 时可以通过加上如下参数来让它首次请求就使用 JIT:

    -v Eval.JitWarmupRequests=0
    

    所以在测试性能时需要注意,运行一两次就拿来对比是看不出效果的。

    类型推导很麻烦,还是逼迫程序员写清楚吧

    JIT 的关键是猜测类型,因此某个变量的类型要是老变就很难优化,于是 HHVM 的工程师开始考虑在 PHP 语法上做手脚,加上类型的支持,推出了一个新语言 - Hack(吐槽一下这名字真不利于 SEO),它的样子如下:

    <?hh
    class Point2 {
      public float $x, $y;
      function __construct(float $x, float $y) {
        $this->x = $x;
        $this->y = $y;
      }
    }
    //来自:https://raw.github.com/strangeloop/StrangeLoop2013/master/slides/sessions/Adams-TakingPHPSeriously.pdf
    

    注意到 float 关键字了么?有了静态类型可以让 HHVM 更好地优化性能,但这也意味着和 PHP 语法不兼容,只能使用 HHVM。

    其实我个人认为这样做最大的优点是让代码更加易懂,减少无意的犯错,就像 Dart 中的可选类型也是这个初衷,同时还方便了 IDE 识别,据说 Facebook 还在开发一个基于 Web 的 IDE,能协同编辑代码,可以期待一下。

    你会使用 HHVM 么?

    总的来说,比起之前的 HPHPc,我认为 HHVM 是值得一试的,它是真正的虚拟机,能够更好地支持各种 PHP 的语法,所以改动成本不会更高,而且因为能无缝切换到官方 PHP 版本,所以可以同时启动 FPM 来随时待命,HHVM 还有FastCGI 接口方便调用,只要做好应急备案,风险是可控的,从长远来看是很有希望的。

    性能究竟能提升多少我无法确定,需要拿自己的业务代码来进行真实测试,这样才能真正清楚 HHVM 能带来多少收益,尤其是对整体性能提升到底有多少,只有拿到这个数据才能做决策。

    最后整理一下可能会遇到的问题,有计划使用的可以参考:

    • 扩展问题:如果用到了 PHP 扩展,肯定是要重写的,不过 HHVM 扩展写起来比 Zend 要简单的多,具体细节可以看 wiki 上的例子
    • HHVM Server 的稳定性问题:这种多线程的架构运行一段时间可能会出现内存泄露问题,或者某个没写好的 PHP 直接导致整个进程挂掉,所以需要注意这方面的测试和容灾措施。
    • 问题修复困难:HHVM 在出现问题时将比 Zend 难修复,尤其是 JIT 的代码,只能期望它比较稳定了。

    P.S. 其实我只了解基本的虚拟机知识,也没写过几行 PHP 代码,很多东西都是写这篇文章时临时去找资料的,由于时间仓促水平有限,必然会有不正确的地方,欢迎大家评论赐教 :)

    2014年1月补充:目前 HHVM 在鄙厂的推广势头很不错,推荐大家在2014年尝试一下,尤其是现在兼容性测试已经达到98.58%了,修改成本进一步减小。

    引用



    2016/8/24  

    今天看了一篇文章,写的不错,拿来分享一下,感谢原作者

    PHP优化对于PHP的优化主要是对php.ini中的相关主要参数进行合理调整和设置,以下我们就来看看php.ini中的一些对性能影响较大的参数应该如何设置。

     # vi /etc/php.ini

    (1) PHP函数禁用找到:

    disable_functions =
    该选项可以设置哪些PHP函数是禁止使用的,PHP中有一些函数的风险性还是相当大的,可以直接执行一些系统级脚本命令,如果允许这些函数执行,当PHP程序出现漏洞时,损失是非常严重的!以下我们给出推荐的禁用函数设置:

    disable_functions = phpinfo,passthru,exec,system,popen,chroot,escapeshellcmd,escapeshellarg,shell_exec,proc_open,proc_get_status

    需注意:如果您的服务器中含有一些系统状态检测的PHP程序,则不要禁用shell_exec,proc_open,proc_get_status等函数。 

    (2) PHP脚本执行时间找到:

    max_execution_time = 30

    该选项设定PHP程序的最大执行时间,如果一个PHP脚本被请求,且该PHP脚本在max_execution_time时间内没能执行完毕,则PHP不再继续执行,直接给客户端返回超时错误。没有特殊需要该选项可保持默认设置30秒,如果您的PHP脚本确实需要长执行时间则可以适当增大该时间设置。 

    (3) PHP脚本处理内存占用找到:

    memory_limit = 8M

    该选项指定PHP脚本处理所能占用的最大内存,默认为8MB,如果您的服务器内存为1GB以上,则该选项可以设置为12MB以获得更快的PHP脚本处理效率。 

    (4) PHP全局函数声明找到:

    register_globals = Off

    网络上很多关于PHP设置的文章都推荐将该选项设置为On,其实这是一种及其危险的设置方法,很可能引起严重的安全性问题。如果没有特殊的需要,强烈推荐保留默认设置! 

    (5) PHP上传文件大小限制找到:

    upload_max_filesize = 2M

    该选项设定PHP所能允许最大上传文件大小,默认为2MB。根据实际应用需求,可以适当增大该设置。 

    (6) Session存储介质找到:

    session.save_path

     

    如果你的PHP程序使用Session对话,则可以将Session存储位置设置为/dev/shm,/dev/shm是Linux系统独有的TMPFS文件系统,是以内存为主要存储方式的文件系统,比RAMDISK更优秀,因为可以使用DISKSWAP作为补充,而且是系统自带的功能模块,不需要另行配置。想想看,从磁盘IO操作到内存操作,速度会快多少?只是需要注意,存储在/dev/shm的数据,在服务器重启后会全部丢失。不过这对于Session来说是无足轻重的。 

     

     

    由于水平有限,有些还是不太明白为什么。如果有更好建议的欢迎随时补充!

    0、用单引号代替双引号来包含字符串,这样做会更快一些。因为PHP会在双引号包围的字符串中搜寻变量,单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的“函数”(译注:PHP手册中说echo是语言结构,不是真正的函数,故把函数加上了双引号)。 
    PS:在单引号中,PHP不会自动搜寻变量、转义字符等,因此效率上快很多。而一般来说字符串是没有变量的,所以使用双引号会导致性能不佳。

    1、如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍。
    PS:事实上,function、method、static method的速度不会有太大差异。具体可见“PHP函数的实现原理及性能分析【转载】一文。

    2、$row[’id’] 的速度是$row[id]的7倍。
    PS:不太懂,貌似差异只有后者会先判断id这个宏是否存在,如果不存在则自动转变为字符串。

    3、echo 比 print 快,并且使用echo的多重参数(译注:指用逗号而不是句点)代替字符串连接,比如echo $str1,$str2。

    PS:如果使用echo $str1.$str2 就会需要 PHP 引擎首先把所有的变量连接起来,然后在输出,而echo $str1,$str2,PHP 引擎就会按照循序输出他们

    4、在执行for循环之前确定最大循环数,不要每循环一次都计算最大值,最好运用foreach代替。
    PS:像count、strlen这样的操作其实是O(1)的,因此不会带来太多消耗,当然避免每次循环都计算是比较好的策略。最好用foreach代替for,这个效率更高,如果考虑到foreach($array as $var)每次拷贝的消耗,可以使用foreach($array as &$var)这样的引用。

    5、注销那些不用的变量尤其是大数组,以便释放内存。
    PS:如果没有记错的话,unset($array)不会立刻释放内存,但随时释放是个好习惯。

    6、尽量避免使用__get,__set,__autoload。

    7、require_once()代价昂贵。
    PS:require_once和include_once需要判重,因此效率上要低,但是5.2版本后效率问题已经基本解决。

    8、include文件时尽量使用绝对路径,因为它避免了PHP去include_path里查找文件的速度,解析操作系统路径所需的时间会更少。
    PS:支持,尽量少用iniset()来设置include_path。

    9、如果你想知道脚本开始执行(译注:即服务器端收到客户端请求)的时刻,使用$_SERVER['REQUEST_TIME']要好于time()。
    PS:$_SERVER['REQUEST_TIME']保存了发起该请求时刻的时间戳,而time()则返回当前时刻的Unix时间戳。

    10、函数代替正则表达式完成相同功能。
    PS:这种函数是指strtok、strstr、strpos、str_replace、substr、explode、implode等等。

    11、str_replace函数比preg_replace函数快,但strtr函数的效率是str_replace函数的四倍。
    PS:字符串操作比正则替换要快。

    12、如果一个字符串替换函数,可接受数组或字符作为参数,并且参数长度不太长,那么可以考虑额外写一段替换代码,使得每次传递参数是一个字符,而不是只写一行代码接受数组作为查询和替换的参数。
    PS:需要考虑到内置函数和用户自定义函数的开销差异,恐怕这种做法得不偿失。

    13、使用选择分支语句(译注:即switch case)好于使用多个if,else if语句。
    PS:php中switch支持数值和字符串变量,比C的switch要好用,建议使用。

    14、用@屏蔽错误消息的做法非常低效,极其低效。
    PS:有什么替代方法吗?没有的话还是不得不用的……

    15、打开apache的mod_deflate模块,可以提高网页的浏览速度。

    16、数据库连接当使用完毕时应关掉,不要用长连接。
    PS:在连接之前,最好设置一下相应的超时机制,例如链接超时、读写超时、等待超时等。

    17、错误消息代价昂贵。

    18、在方法中递增局部变量,速度是最快的。几乎与在函数中调用局部变量的速度相当。

    19、递增一个全局变量要比递增一个局部变量慢2倍。

    20、递增一个对象属性(如:$this->prop++)要比递增一个局部变量慢3倍。

    21、递增一个未预定义的局部变量要比递增一个预定义的局部变量慢9至10倍。

    22、仅定义一个局部变量而没在函数中调用它,同样会减慢速度(其程度相当于递增一个局部变量)。PHP大概会检查看是否存在全局变量。

    23、方法调用看来与类中定义的方法的数量无关,因为我(在测试方法之前和之后都)添加了10个方法,但性能上没有变化。

    24、派生类中的方法运行起来要快于在基类中定义的同样的方法。

    25、调用带有一个参数的空函数,其花费的时间相当于执行7至8次的局部变量递增操作。类似的方法调用所花费的时间接近于15次的局部变量递增操作。

    26、Apache解析一个PHP脚本的时间要比解析一个静态HTML页面慢2至10倍。尽量多用静态HTML页面,少用脚本。

    27、除非脚本可以缓存,否则每次调用时都会重新编译一次。引入一套PHP缓存机制通常可以提升25%至100%的性能,以免除编译开销。

    28、尽量做缓存,可使用memcached。memcached是一款高性能的内存对象缓存系统,可用来加速动态Web应用程序,减轻数据库负载。对运算码 (OP code)的缓存很有用,使得脚本不必为每个请求做重新编译。

    29、当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用strlen()函数。此函数执行起来相当快,因为它不做任何计算,只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储的已知字符串长度。但是,由于strlen()是函数,多多少少会有些慢,因为函数调用会经过诸多步骤,如字母小写化(译注:指函数名小写化,PHP不区分函数名大小写)、哈希查找,会跟随被调用的函数一起执行。在某些情况下,你可以使用isset() 技巧加速执行你的代码。

      (举例如下)

      if (strlen($foo) < 5) { echo “Foo is too short”$$ }

      (与下面的技巧做比较)

      if (!isset($foo{5})) { echo “Foo is too short”$$ }

      调用isset()恰巧比strlen()快,因为与后者不同的是,isset()作为一种语言结构,意味着它的执行不需要函数查找和字母小写化。也就是说,实际上在检验字符串长度的顶层代码中你没有花太多开销。
    PS:长见识了。

    30、当执行变量$i的递增或递减时,$i++会比++$i慢一些。这种差异是PHP特有的,并不适用于其他语言,所以请不要修改你的C或Java代码并指望它们能立即变快,没用的。++$i更快是因为它只需要3条指令(opcodes),$i++则需要4条指令。后置递增实际上会产生一个临时变量,这个临时变量随后被递增。而前置递增直接在原值上递增。这是最优化处理的一种,正如Zend的PHP优化器所作的那样。牢记这个优化处理不失为一个好主意,因为并不是所有的指令优化器都会做同样的优化处理,并且存在大量没有装配指令优化器的互联网服务提供商(ISPs)和服务器。

    31、并不是事必面向对象(OOP),面向对象往往开销很大,每个方法和对象调用都会消耗很多内存。

    32、并非要用类实现所有的数据结构,数组也很有用。

    33、不要把方法细分得过多,仔细想想你真正打算重用的是哪些代码?

    34、当你需要时,你总能把代码分解成方法。
    PS:分解成方法要适当,行数少使用频率高的方法尽量用直接写代码,可以减少函数堆栈开销;且方法嵌套不宜过深,否则大大影响PHP的运行效率。

    35、尽量采用大量的PHP内置函数。

    36、如果在代码中存在大量耗时的函数,你可以考虑用C扩展的方式实现它们。

    37、评估检验(profile)你的代码。检验器会告诉你,代码的哪些部分消耗了多少时间。Xdebug调试器包含了检验程序,评估检验总体上可以显示出代码的瓶颈。

    38、mod_zip可作为Apache模块,用来即时压缩你的数据,并可让数据传输量降低80%。

    39、在可以用file_get_contents替代file、fopen、feof、fgets等系列方法的情况下,尽量用file_get_contents,因为他的效率高得多!但是要注意file_get_contents在打开一个URL文件时候的PHP版本问题;
    PS:这个要记住,尽量使用file_get_contents和file_put_contents,不需要自己判断文件句柄打开是否成功。

    40、尽量的少进行文件操作,虽然PHP的文件操作效率也不低的;

    41、优化Select SQL语句,在可能的情况下尽量少的进行Insert、Update操作(在update上,我被恶批过);

    42、尽可能的使用PHP内部函数(但是我却为了找个PHP里面不存在的函数,浪费了本可以写出一个自定义函数的时间,经验问题啊!);
    PS:内置函数比用户自定义函数效率高了将近一个数量级。

    43、循环内部不要声明变量,尤其是大变量:对象(这好像不只是PHP里面要注意的问题吧?);
    PS:这个必须的,变量过多或者过大时,每次重分配的开销就无法忽略。

    44、多维数组尽量不要循环嵌套赋值;

    45、在可以用PHP内部字符串操作函数的情况下,不要用正则表达式;

    46、foreach效率更高,尽量用foreach代替while和for循环;

    47、用单引号替代双引号引用字符串;
    PS:晕,这个不就是第一条吗?

    48、“用i+=1代替i=i+1。符合c/c++的习惯,效率还高”;

    49、对global变量,应该用完就unset()掉;







       

        

    展开全文
  • WordPress是当今最流行的建站博客程序,功能强大,上手容易,各种主题和插件等应有尽有,有关Wordpress的相关文档也是多如牛毛,可以说Wordpress...优化Wordpress性能,一般我们是从这几个方面来发力:服务器PHP脚本执
  • 主要介绍了php导入大量数据到mysql性能优化技巧,通过针对SQL语句的优化实现了mysql性能的提高,非常具有实用价值,需要的朋友可以参考下
  • 提高 宝塔面板的服务器而言就相对简单多了,今天...为了让服务器运行速度更快,我们就使用宝塔面板提供的功能来优化一下服务器配置。一、定期释放内存添加计划任务,可以设置每天或一周释放一次,间隔时间根据自己...
  • 看完Pangee老师的PHP性能优化,做笔记。 一、性能检测工具 1、xhprof工具分析PHP性能 安装:从其他帖子学习安装。 先有个线上能访问的站点用于性能优化。 检查是否支持xhprof 项目中添加xhprof代码 ...
  • 优化php效率,提高php性能的一些方法,开发php的朋友可以注意下。
  • PHP性能优化方案

    千次阅读 2018-07-28 10:16:29
    常用性能优化方案 1.使用单引号替换双引号,单引号在运行的时候不检查运行引号内部的变量,执行效率是双引号的两倍; 2.使用PHP内置的数组操作方法,PHP内置的数组操作方法的运行效率是自行编写代码的10倍以上; 3....
  • 主要介绍了PHP+swoole+linux实现系统监控和性能优化操作,结合实例形式分析了php启动swoole及Linux性能监控相关操作技巧,需要的朋友可以参考下
  • 性能优化是我们开发中必不可少的一部分,下面这篇文章主要给大家介绍了关于PHP中你可能忽略的性能优化利器:生成器的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面来一起看看吧。
  • 本文给大家分享五个PHP7性能优化提升技巧,对php7性能提升相关知识感兴趣的朋友一起学习吧
  • PHP性能优化小技巧

    千次阅读 2017-06-19 09:59:51
    语法吸收了C语言、java和Perl的特点,利于学习,使用广泛,主要适用于Web开发领域。... 对于刚接触php的学员,这里我跟大家分享10条PHP性能优化的小技巧,帮助你更好的用PHP开发:  1. foreach效率更高,尽量用
  • mysql 性能优化 PHP 高级开发工程师 架构设计
  • PHP 应用性能优化实践 1. benchmarkding 2. improving client downloading 3. php code optimizing 4. opcode caching 5. variable caching ......

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 117,329
精华内容 46,931
关键字:

php性能优化