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

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

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

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

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

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

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

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

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

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

图1、PHP 语言解析运行过程

图1、PHP 语言解析运行过程

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

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

图2. 启用了 opcode 缓存的 PHP 运行过程

图2. 启用了 opcode 缓存的 PHP 运行过程

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

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

图3. 没有使用Opcode缓存的请求,20个并发,10000次

图3. 没有使用Opcode缓存的请求,20个并发,10000次

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

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

图4、启用APC 缓存加速的实验结果

图4、启用APC 缓存加速的实验结果

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

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

图5、启用OpCode Cache 的性能分析结果

图5、启用OpCode Cache 的性能分析结果

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

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

图6、vld 扩展反编译出来的PHP代码的字节码

图6、vld 扩展反编译出来的PHP代码的字节码

可以看到每一行代码被编译成相应的 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)

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/d785bb936ec50e79c39c55e494f540d3.html