热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

PHP性能分析与实验——性能的宏观分析

此前,阅读过了很多关于PHP性能分析的文章,不过写的都是一条一条的规则,而且,这些规则并没有上下文,也没有明确的实验来体现出这些规则的优势,同时讨论的也侧重于一些语法要点。本文就改变PHP性能分析的角度,并通过实例来分析出PHP的性能方面需要注意和改进的点。

本文来源:细说PHP - http://www.xishuophp.com/news/5101.html

此前,阅读过了很多关于 PHP 性能分析的文章,不过写的都是一条一条的规则,而且,这些规则 并没有上下文,也没有明确的实验来体现出这些规则的优势,同时讨论的也侧重于一些语法要点。本文就 改变 PHP 性能分析的角度,并通过实例来分析出 PHP 的性能方面需要注意和改进的点。

对 PHP 性能的分析,我们从两个层面着手,把这篇文章也分成了两个部分,一个是宏观层面,所 谓宏观层面,就是 PHP 语言本身和环境层面,一个是应用层面,就是语法和使用规则的层面,不过不仅探 讨规则,更辅助以示例的分析。

宏观层面,也就是对 PHP 语言本身的性能分析又分为三个方面:

  1. PHP 作为解释性语言性能有其天然的缺陷

  2. PHP 作为动态类型语言在性能上也有提升的空间

  3. 当下主流 PHP 版本本身语言引擎性能

  4. 一、PHP 作为解释性语言的性能分析与提升

PHP 作为一门脚本语言,也是解释性语言,是其天然性能受限的原因,因为同编译型语言在运行 之前编译成二进制代码不同,解释性语言在每一次运行都面对原始脚本的输入、解析、编译,然后执行。 如下是 PHP 作为解释性语言的执行过程。

如上所示,从上图可以看到,每一次运行,都需要经历三个解析、编译、运行三个过程。

那优化的点在哪里呢?可以想见,只要代码文件确定,解析到编译这一步都是确定的,因为文件 已不再变化,而执行,则由于输入参数的不同而不同。在性能优化的世界里,至上绝招就是在获得同样结 果的情况下,减少操作,这就是大名鼎鼎的缓存。缓存无处不在,缓存也是性能优化的杀手锏。于是乎 OpCode 缓存这一招就出现了,只有第一次需要解析和编译,而在后面的执行中,直接由脚本到 Opcode, 从而实现了性能提速。执行流程如下图所示:

相对每一次解析、编译,读到脚本之后,直接从缓存读取字节码的效率会有大幅度的提升,提升 幅度到底有多大呢?

我们来做一个没有 Opcode 缓存的实验。20 个并发,总共 10000 次请求没有经过 opcode 缓存 的请求,,得到如下结果:

其次,我们在服务器上打开 Opcode 缓存。要想实现 opcode 缓存,只需要安装 APC、Zend OPCache、eAccelerator 扩展即可,即使安装了多个,也只启用其中一个。注意的是,修改了 php.ini 配 置之后,需要重新加载 php-fpm 的配置。

这里分别启用 APC 和 Zend OPCache 做实验。启用 APC 的版本。

可以看到,速度有了较大幅度的提升,原来每个请求 110ms,每秒处理请求 182 个,启用了 APC 之后 68ms,每秒处理请求 294 个,提升速度将近 40%。

在启用了 Zend Opcache 的版本中,得到同 APC 大致相当的结果。每秒处理请求 291 个,每请 求耗时 68.5ms。

从上面的这个实验可以看到,所用的测试页面,有 40ms 以上的时间花在了语法解析和编译这两 项上。通过将这两个操作缓存,可以将这个处理过程的速度大大提升。

这里附加补充一下,OpCode 到底是什么东东,OpCode 编译之后的字节码,我们可以使用bytekit 这样的工具,或者使用 vld PHP 扩展来实现对 PHP 的代码编译。如下是 vld 插件解析代码的运行结果。

可以看到每一行代码被编译成相应的 OpCode 的输出。

二、PHP 作为动态类型语言的性能分析与改进

第二个是 PHP 语言是动态类型的语言,动态类型的语言本身由于涉及到在内存中的类型推断,比 如在 PHP 中,两个整数相加,我们能得到整数值,一个整数和一个字符串相加,甚至两个字符串相加,都 变成整数相加。而字符串和任何类型连接操作都成了字符串。

运行结果如下:

float(40.11) int(40) int(30) int(30)

语言的动态类型为开发者提供了方便,语言本身则会因为动态类型而降低效率。在 Swift 中,有 一个特性叫类型推断,我们可以看看类型推断会带来多大的一个效率上的差别呢?对于需要类型推断与不 需要类型推断两段 Swift 代码,我们尝试编译一下看看效果如何。 第一段代码如下:

这是一段 Swift 代码,字典只有 14 个键值对,这段代码的编译,9 分钟了还没有编译完成(5G 内存,2.4GHz CPU),编译环境为 Swift 1.2,Xcode 6.4。

但是如果调整代码如下:

也就是加上了类型限定,避免了 planeLocation 的类型推断。编译过程花了 2S 。

可见,作为动态类型附加的类型推断操作极大地降低了程序的编译速度。 当然,这个例子有点极 端,用 Swift 来类比 PHP 也不一定合适,因为 Swift 语言本身也还在不断的进化过程中。本例子只是表 明在编程语言中,如果是动态类型语言,就涉及到对动态类型的处理,从编译的角度讲是会受影响的。

那么作为动态类型的 PHP 的效率如何提升呢?从 PHP 语言本身这个层面是没有办法解决的,因 为你怎么写也是动态类型的代码。解决办法就是将PHP转化为静态类型的表示,也就是做成扩展,可以看到 ,鸟哥的很多项目,比如 Yaf 框架,都是做成了扩展的,当然这也是由于鸟哥是 C 高手。扩展由于是 C 或者 C++ 而写,所以不再是动态类型,又加之是编译好的,而 C 语言本身的效率也会提升很多。所以效 率会大幅度提高。

下面我们来看一段代码,这段代码,只是实现了简单的素数运算,能计算指定值以内的素数个数 ,用的是普通的筛选法。现在看看扩展实现,跟 PHP 原生实现的效率差别,这个差别当然,不仅仅是动态 类型和编译类型的差别,还有语言效率的差别。

首先是用纯 PHP 写成的算法,计算 1000 万以内的素数个数,耗时在 33s 上下,实验了三次, 得到的结果基本相同。

其次,我们将这个求素数个数的过程,编写成了 PHP 扩展,在扩展中实现了 getprimenumbers 函数,输入一个整数,返回小于该整数的素数。得到的结果如下,这个效率的 提升是非常惊人的,在 1.4s 上下即返回。速度提升 20 倍以上。

可以想见,静态和编译类型的语言,其效率得到了惊人的提升。本程序的 C 语言代码如下:

PHP_FUNCTION(get_prime_numbers) { long value; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &value) == FAILURE) { return; } int *numbers = (int *)malloc(sizeof(int)*128*10000); memset(numbers, 0x0, 128*10000); int num = 2; numbers[0] = 2; numbers[1] = 3; bool flag = true; double f = 0; int i = 0; int j = 0; for(i=5; i<=value; i+=2) { flag = true; f = sqrt(i); for(j=0; jf) { break; } } if(flag) { numbers[num] = i; num++; } } free(numbers); RETURN_LONG(num); }

三、PHP 语言本身底层性能引擎提升

第三个性能优化层面是语言本身的性能提升,这个就不是我们普通开发者所能做的了。在 PHP 7 以前,寄希望于小版本的改进,但是改进幅度不是非常的显著,比如 PHP 5.3 、PHP 5.4、PHP 5.5、PHP 5.5 对同一段代码的性能比较,有一定程度的进步。

PHP 5.3 的版本在上面的例子中已讲过,需要 33s 左右的时间,我们现在来看别的PHP版本。分 别运行如下:

PHP 5.4 版,相较 5.3 版已经有一定程度的提升。快 6 秒左右。

PHP 5.5 版在 PHP 5.4的基础上又进了一步,快了 6S。

PHP5.6 反而有些退步。

PHP 7 果真是效率提升惊人,是 PHP5.3 的 3 倍以上。

以上是求素数脚本在各个 PHP 版本之间的运行速度区别,尽管只测试了这一个程序,也不是特别 的严谨,但是这是在同一台机器上,而且编译 configure 参数也基本一样,还是有一定可比性的。

在宏观层面,除了上面的这些之外,在实际的部署过程中,对 PHP 性能的优化,还体现为要减少 在运行中所消耗的资源。所以 FastCGI 模式和 mod_php 的模式比传统的 CGI 模式也更为受欢迎。因为在 传统的 CGI 模式中,在每一次脚本运行都需要加载所有的模块。而在程序运行完成了之后,也要释放模块 资源。如下图所示:

而在 FastCGI 和 mod_php 模式中,则不需要如此。只有 php-fpm 或者 Apache 启动的时候,需 要加载一次所有的模块,在具体的某次运行过程中,并不需要再次加载和释放相关的模块资源。

这样程序性能的效率得到了提升。以上就是有关 PHP 宏观层面的性能优化的分析,在本文的第二 部分我们将探讨应用方面的 PHP 优化准则。敬请期待!


推荐阅读
  • 如何实现织梦DedeCms全站伪静态
    本文介绍了如何通过修改织梦DedeCms源代码来实现全站伪静态,以提高管理和SEO效果。全站伪静态可以避免重复URL的问题,同时通过使用mod_rewrite伪静态模块和.htaccess正则表达式,可以更好地适应搜索引擎的需求。文章还提到了一些相关的技术和工具,如Ubuntu、qt编程、tomcat端口、爬虫、php request根目录等。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • Monkey《大话移动——Android与iOS应用测试指南》的预购信息发布啦!
    Monkey《大话移动——Android与iOS应用测试指南》的预购信息已经发布,可以在京东和当当网进行预购。感谢几位大牛给出的书评,并呼吁大家的支持。明天京东的链接也将发布。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • 本文介绍了使用AJAX的POST请求实现数据修改功能的方法。通过ajax-post技术,可以实现在输入某个id后,通过ajax技术调用post.jsp修改具有该id记录的姓名的值。文章还提到了AJAX的概念和作用,以及使用async参数和open()方法的注意事项。同时强调了不推荐使用async=false的情况,并解释了JavaScript等待服务器响应的机制。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
author-avatar
mobiledu2502928043
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有