Practice make perfect

PHP 性能优化

性能问题原因

  • PHP 语法使用不恰当
  • 使用 PHP 语言做了它不擅长的事情
  • 用 PHP 语言连接的服务不给力
  • PHP 自身的短板(不能做)
  • 未知问题

分析

PHP 性能问题占项目性能问题 30~40% (优化项目不能局限于 PHP)

  • PHP 语言级的性能优化
  • PHP 周边问题的性能优化
  • PHP 语言自身分析、优化

从上往下优化难度从简单到困难

工具

$ ab -n100 -c10 https://www.baidu.com/
...
# 尽可能多
Requests per second:    147.66 [#/sec] (mean)
# 尽可能小
Time per request:       6.773 [ms] (mean, across all concurrent requests)
...

语言级性能优化

少写代码,多用 PHP 自身能力

性能问题:

  • 自写代码冗余较多,可读性不佳,并且性能低。

为什么性能低?

  • PHP 代码需要编译解析为底层语言,这一过程每次请求都会处理一遍 ,开销大。

好的方法:

  • 多使用PHP内置变量、常量、函数

PHP代码运行流程

PHP 内置函数性能优劣

情况描述:

  • PHP内置函数,之间依然存在快慢差异

好的建议:

  • 多去了解PHP内置函数的时间复杂度

举例:

  • isset()array_key_ exists()方法间的性能差异(isset性能更优)

尽可能少用魔法函数

情况描述:

  • PHP提供的魔法函數,性能不佳

为什么性能低?

  • 为了给PHP程序员省事, PHP语言为 你做了很多

好的方法:

  • 尽可能规避使用PHP魔法函数

举例:

测试魔法函数 __get() 性能

$ time php test_magic.php
real Om0.020s
user Om0.017s # php 执行耗时
sys  Om0.002s

产生额外开销的错误抑制符@

情况描述:

  • PHP提供的错误抑制符只是为了方便“懒人”

@的实际逻辑:

  • 在代码开始前、结束后,增加Opcode,忽略报错

好的建议:

  • 建议尽量不要使用@错误抑制符,使用try...catch

vld - PHP Opcode查看扩展

$ php -dvld. active=1 -dvld. execute=O at.php
# execute 0 查看Opcode代码 不执行

合理使用内存

情况描述:

  • PHP有内存回收机制保底,但也请小心使用内存

好的建议:

  • 利用unset()及时释放不使用的内存( 注: unset()出现注销不掉的情况)

尽量少的使用正则表达式

情况描述:

  • 正则表达式的回溯开销较大,“没有金刚钻别揽瓷器活”

好的建议:

  • 利用字符串处理函数,实现相同逻辑

避免在循环内做运算

情况描述:

  • 循环内的计算式将会被重复计算
<?php
$str = "hello world";
$strlen = strlen($str) // 避免重复计算
for($i = 0;$i < $strlen;$i++)
//do something

减少计算密集型业务

情况描述:

  • PHP不适合密集型运算的场景

原因

  • PHP语言特性决定了PHP不适合做大数据量运算

PHP适用场景:

  • 适合衔接Webserver与后端服务、UI呈现

务必使用带引号字符串做键值

情况描述:

  • PHP会将没有引号的键值当作常量,产生查找常量的开销

好的建议:

  • 严格使用引号作为键值
<?php
  //define("key", "imooc") ;
  $array = array(
    "key" => "hello world!",
    "imooc" => "http://www.imooc.com/",
  );
  echo $array["key"] // 若不加"" 且未定义key,会Notice 但有值,此时有额外开销

PHP周边问题的性能优化

PHP周边:

  • Linux运行环境
  • 文件存储
  • 数据库
  • 缓存
  • 网络

找出问题核心,抓大头优化

减少文件类操作

常见PHP场景的开销次序:

读写网络数据 > 读写磁盘 > 读写数据库 >> 读写内存

优化网络请求

网络请求的坑:

  1. 对方接口的不确定因素
  2. 网络稳定性

如何优化网络请求?

  1. 设置超时时间
    a) 连接超时 200ms
    b) 读超时 800ms
    c) 写超时 500ms

  2. 将串行请求并行化

    a) 使用 curl_multi_ *() (返回依赖最长的 时间)
    b) 使用swoole扩展

压缩PHP接口输出

如何压缩?

  • 使用Gzip即可

压缩输出的利与弊?

  • : 利于我们的数据输出, Client端能更快获取数据
  • : 额外的CPU开销

缓存重复计算内容

什么情况下做输出内容的缓存?

  • 多次请求,内容不变情况

PHP缓存流程

例如:模板缓存

重叠时间窗口思想

重叠时间窗口

旁路方案:

旁路方案

PHP 性能分析

工具:

  • XHPorf ( 源自Fackbook的PHP性能分析工具)
$ php --ri xhprof # 查看拓展是否支持
xhprof
xhprof => 0.9.2
CPU num  => 1

index.php

<?php
xhprof_enable();
...
$data = xhprof_disable();
include_once "/var/www/html/xhprof_lib/utils/xhprof_ lib.php"
include_once "var/www/html/xhprof_lib/utils/xhprof_runs.php"
$objXhprofRun = new XHProfRuns_Default();
$run_id = $objXhprofRun-> save_run($data, "test");
var_dump($run_id);

查看流程图,分析红色区域即耗时最长的函数

其他方法

  • Opcode Cache : PHP 扩展 APC
  • 扩展实现:通过PHP扩展代替原PHP代码中高频逻辑
  • Runtime 优化: HHVM

评论