Wicky's Blog

PHP性能优化-XHProf

背景

最近在做关于项目的接口性能优化的相关工作,于是想到了Xhprof,决定把它用在现有项目中,观察下接口的调用情况。

xhprof是一个函数级别的分层PHP性能分析工具,xhprof能统计每个函数的调用次数,cpu使用时间内存占用等多项指标,它将函数的开销,细分为调用者和被调用者的开销。展示页面基于浏览器非常人性化,分为两个性能报告页面,一个是报表格式的一个是直观化的图表格式。根据这些分析数据,可以轻松的看到程序的开销情况,找出低效率的程序进行优化。

在安装的过程中,踩了一些坑,总结一下经验。

注:对于应该不应该将XHProf部署到生产环境,还不知晓,但是官方说可以,但是最好是采样模式(1/1000请求)进行尝试,以降低服务器资源开销。

安装PHP的xhprof扩展

从官网下载的安装包(wget http://pecl.php.net/get/xhprof-0.9.2.tgz),安装的过程中,make && make install会有[xhprof.lo] Error 1的报错。
所以这里建议在github下载安装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
git clone https://github.com/phacility/xhprof
cd xhprof/extension
/usr/local/bin/phpize
./configure --with-php-config=/usr/local/bin/php-config
make && make install
[php.ini]
在编译完成之后,需要在xhprof运行时配置(php.ini)
在php.ini中加入
extension=xhprof.so
xhprof.output_dir=/usr/local/var/www/xhprof
这个目录是用来存放分析的结果数据

在安装完成之后,可通过 php –ri xhprof 查看是否安装成功。

1
2
3
4
php --ri xhprof
xhprof => 0.9.2
CPU num => 4

此时说明扩展已经安装成功

安装Graphviz

将XHProf的分析报告以程序的函数调用图来展现。

1
2
3
4
5
wget http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.34.0.tar.gz
tar zxvf graphviz-2.34.0.tar.gz
cd graphviz-2.34.0
./configure
make && make install

如加上–prefix编译参数,将安装完的主目录中的bin目录路径加入到环境变量PATH中,另外需要注意的如果通过XHProf提供的PHP UI界面查看函数调用图,需要将PHP的proc_open类的函数打开。

运行Xhprof

在服务器或本地的web目录下,新建xhprof目录,然后将下载的xhprof项目的xhprof_html、xhprof_lib、examples放到新建的xhprof中。

1
cp -R /Downloads/xhprof /usr/local/var/www/work/xhprof

在根目录下在创建目录,用来存放分析结果:

1
mkdir -P /usr/local/var/www/xhprof

然后在Nginx及FPM增加以下配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
nginx.conf:
server {
listen 80;
server_name xhprof.dev;
root /usr/local/var/www/work/xhprof;
index index.php index.html;
access_log /usr/local/var/log/nginx/xhprof.dev.log;
error_log /usr/local/var/log/nginx/xhprof.dev.log.err;
location ~* \.php$ {
fastcgi_pass 127.0.0.1:9003;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_index index.php;
include fastcgi_params;
}
}
php-fpm:
[xhprof.dev]
user = www
group = www
listen = 127.0.0.1:9003
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 512
pm.start_servers = 32
pm.min_spare_servers = 32
pm.max_spare_servers = 64
pm.max_requests = 1500
pm.status_path = /h3_monitor
slowlog = /usr/local/var/log/$pool-slow_log
request_slowlog_timeout = 3
request_terminate_timeout = 20
catch_workers_output = no
security.limit_extensions = ""
env[XHPROF_ROOT_PATH] = /usr/local/var/www/work/xhprof/
;此配置是让xhprof加入到环境变量中使用

安装完成后,需要重启nginx及php-fpm:

1
2
/usr/local/sbin/php-fpm restart
nginx -s reload

在浏览器中输入:
http://xhprof.dev/examples/sample.php,有如下的结果:

1
Array ( [foo==>bar] => Array ( [ct] => 5 [wt] => 147 ) [foo==>strlen] => Array ( [ct] => 5 [wt] => 6 ) [bar==>bar@1] => Array ( [ct] => 4 [wt] => 19 ) [bar@1==>bar@2] => Array ( [ct] => 3 [wt] => 3 ) [bar@2==>bar@3] => Array ( [ct] => 2 [wt] => 1 ) [bar@3==>bar@4] => Array ( [ct] => 1 [wt] => 0 ) [main()==>foo] => Array ( [ct] => 1 [wt] => 276 ) [main()==>xhprof_disable] => Array ( [ct] => 1 [wt] => 0 ) [main()] => Array ( [ct] => 1 [wt] => 287 ) ) --------------- Assuming you have set up the http based UI for XHProf at some address, you can view run at http://xhprof.dev/index.php?run=58ec5faa0034c&source=xhprof_foo ---------------

在/usr/local/var/www/xhprof/output下生产下面的文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
➜ www cd xhprof/output
➜ output ll
total 256
-rw-r--r-- 1 Wicky admin 308 4 10 21:05 58eb8332ad434.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 486 4 10 21:07 58eb8374b2143.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 307 4 11 10:03 58ec3966e0a06.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 483 4 11 10:03 58ec396f59585.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 483 4 11 10:03 58ec39727a2da.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 307 4 11 10:03 58ec397b5d9b6.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 307 4 11 11:05 58ec47e69431d.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 308 4 11 11:05 58ec47e7a6c41.xhprof_foo.xhprof
-rw-r--r-- 1 Wicky admin 38529 4 11 11:15 58ec4a5f42fab.xhprof.xhprof
-rw-r--r-- 1 Wicky admin 49627 4 11 11:24 58ec4c6a1876f.xhprof.xhprof
-rw-r--r-- 1 Wicky admin 483 4 11 12:46 58ec5faa0034c.xhprof_foo.xhprof

选择生产的id,在浏览器中可观察运行分析结果:
http://xhprof.dev/xhprof_html/?run=58ec4c6a1876f&symbol=Fw%5CApp%3A%3Arun

项目使用

由于使用的公司项目,但是思路都差不多:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
xhprof_enable(XHPROF_FLAGS_NO_BUILTINS | XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY);
#核心文件的执行 站点的配置
...
require 'index.php'
...
$xhprof_data = xhprof_disable();
include_once $_SERVER['XHPROF_ROOT_PATH'] . "/xhprof_lib/utils/xhprof_lib.php";
include_once $_SERVER['XHPROF_ROOT_PATH'] . "/xhprof_lib/utils/xhprof_runs.php";
$xhprof_runs = new XHProfRuns_Default();
$run_id = $xhprof_runs->save_run($xhprof_data, "xhprof");
echo 'http://xhprof.dev/xhprof/xhprof_html/index.php?run=' . $run_id . '&source=xhprof_test';

变量$run_id是本次请求生成分析结果的id,最后我们输出了一个链接地址,使用改地址就可以看到本次请求的分析结果。

结果报告:
image
关系调用:
image

图中红色的部分为性能比较低,耗时比较长的部分,我们可以根据根据哪些函数被标记为红色对系统的代码进行优化。

Xhprof 报告字段含义

Function Name:方法名称。

Calls:方法被调用的次数。

Calls%:方法调用次数在同级方法总数调用次数中所占的百分比。

Incl.Wall Time(microsec):方法执行花费的时间,包括子方法的执行时间。(单位:微秒)

IWall%:方法执行花费的时间百分比。

Excl. Wall Time(microsec):方法本身执行花费的时间,不包括子方法的执行时间。(单位:微秒)

EWall%:方法本身执行花费的时间百分比。

Incl. CPU(microsecs):方法执行花费的CPU时间,包括子方法的执行时间。(单位:微秒)

ICpu%:方法执行花费的CPU时间百分比。

Excl. CPU(microsec):方法本身执行花费的CPU时间,不包括子方法的执行时间。(单位:微秒)

ECPU%:方法本身执行花费的CPU时间百分比。

Incl.MemUse(bytes):方法执行占用的内存,包括子方法执行占用的内存。(单位:字节)

IMemUse%:方法执行占用的内存百分比。

Excl.MemUse(bytes):方法本身执行占用的内存,不包括子方法执行占用的内存。(单位:字节)

EMemUse%:方法本身执行占用的内存百分比。

Incl.PeakMemUse(bytes):Incl.MemUse峰值。(单位:字节)

IPeakMemUse%:Incl.MemUse峰值百分比。

Excl.PeakMemUse(bytes):Excl.MemUse峰值。单位:(字节)

EPeakMemUse%:Excl.MemUse峰值百分比。
可以根据上述字段查找自己要看的数据,然后进行优化

其他

可使用xhgui/xhprof.io等工具来分析xhprof结果。

参考资料

xhprof地址 https://github.com/facebook/xhprof

xhprof手册 http://php.net/xhprof

xhprof.io https://github.com/gajus/xhprof.io

xhgui https://github.com/perftools/xhgui

oneapm http://www.oneapm.com/