Loading... # [使用XHProf查找PHP性能瓶颈](https://segmentfault.com/a/1190000003509917) XHProf是facebook 开发的一个测试php性能的扩展,本文记录了在PHP应用中使用XHProf对PHP进行性能优化,查找性能瓶颈的方法。 #### 下载 网上很多是编译安装xhprof-0.9.4版本,应该是用php5,在php8.0下编译xhprof-0.9.4各种报错(编译安装这个拓展 [tideways](https://github.com/tideways/php-xhprof-extension) 不会报错),php7、php8环境下,建议下载最新版本 [https://pecl.php.net/package/xhprof](https://pecl.php.net/package/xhprof) ## 安装Xhprof扩展 ```bash cd /opt wget https://pecl.php.net/get/xhprof-2.3.9.tgz tar -zxvf xhprof-2.3.9.tgz cd xhprof-2.3.9 cd extension/ phpize ./configure make && make install ``` 修改`php.ini` ```php [xhprof] extension=xhprof.so xhprof.output_dir=/tmp ``` > 配置中`xhprof.output_dir`指定了生成的profile文件存储的位置,我们将其指定为/tmp。 ## 对PHP进行性能分析 在XHProf扩展中,一共提供了四个函数用于对PHP进行性能分析。 `xhprof_enable/xhprof_sample_enable`函数用于开始XHProf性能分析,区别在于前者功能更加强大,而后者则是是以简单模式启动性能分析(简单记录了函数的调用栈信息),开销比较小。 `xhprof_disable/xhprof_sample_disable`函数用于停止性能分析,并返回分析的数据。 需要特别说明的函数是`xhprof_enable`,其他函数都是不需要提供参数的,而该函数则可以接受两个可选的参数,用于改变该工具的行为。 ```php void xhprof_enable ([ int $flags = 0 [, array $options ]] ) ``` * flags 该参数用于为剖析结果添加额外的信息,该参数的值使用以下宏,如果需要提供多个值,使用`|`进行分隔。 * XHPROF_FLAGS_NO_BUILTINS 跳过所有的内置函数 * XHPROF_FLAGS_CPU 添加对CPU使用的分析 * XHPROF_FLAGS_MEMORY 添加对内存使用的分析 * options 数组形式提供可选参数,在此处提供`ignored_functions`选项需要忽略的函数 比如下面的例子,同时对内存和CPU进行分析,并且忽略对`call_user_func`和`call_user_func_array`函数的分析。 ```php xhprof_enable( XHPROF_FLAGS_MEMORY|XHPROF_FLAGS_CPU, [ 'ignored_functions' => [ 'call_user_func', 'call_user_func_array' ] ] ); // 这里是PHP代码,比如业务逻辑实现等要被分析的代码部分 .... $xhprofData = xhprof_disable();// $xhprofData是数组形式的分析结果 print_r($xhprofData); ``` > 注意,如果使用`XHPROF_FLAGS_CPU`选项对CPU占用也进行分析,在Linux环境下,会造成比较高的系统负载,因此不建议使用,而推荐只使用`XHPROF_FLAGS_MEMORY`,对内存的分析不会对系统造成太多负载。 ## 形象化的查看分析结果 ### 安装graphviz 使用`xhprof_disable`完成性能分析并且获取到分析结果之后,我们通常不会直接输出结果,因为这样的结果是以数组形式组织的,看起来并不直观,幸运的是,xhprof提供了基于web的图形界面对分析结果进行查看。 在使用之前,请先确保服务器安装了`graphviz`工具,否则在生成监控图表的时候回出现以下错误: ```bash failed to execute cmd: " dot -Tpng". stderr: `sh: dot: command not found ' ``` 这里提示找不到`dot`命令,所以需要先安装`graphviz` ```bash yum -y install graphviz ``` ### 将xhprof安装包中的xhprof_html_和xhproflib目录放到服务器的web目录下 由于分析结果的查看工具是基于web的,因此,我们需要将xhprof安装包中的xhprof_html_和xhproflib目录放到服务器的web目录下,让xhprof_html目录中的内容对外可以访问。 比如我的测试服务器环境是使用vagrant搭建的CentOS,这两个目录放到/opt/xhprof-2.3.9目录下: ```bash cp -r /opt/xhprof-2.3.9/xhprof_html/ /www/nginx/php/xhprof_html cp -r /opt/xhprof-2.3.9/xhprof_lib/ /www/nginx/php/xhprof_lib ``` web服务器使用的是Nginx,因此,修改Nginx的配置文件`nginx.conf`中的配置如下: ```bash server { location / { root /www/nginx/php;#等于php; index index.php index.html index.htm; } location ~ \.php$ { root /www/nginx/php; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #支持解析php文件 include fastcgi_params; } ``` ### 使用 修改我们的代码,是其能够将分析结果存放到`xhprof.output_dir`(上面在php.ini设置的xhprof.output_dir=/tmp)指定的目录中。 #### 在 PHP 页面顶部加上: ```php xhprof_enable(XHPROF_FLAGS_NO_BUILTINS + XHPROF_FLAGS_MEMORY); ``` #### 在页面底部加上: ```bash $xhprofData = xhprof_disable(); require '/vagrant/xhprof/xhprof_lib/utils/xhprof_lib.php'; require '/vagrant/xhprof/xhprof_lib/utils/xhprof_runs.php'; $xhprofRuns = new XHProfRuns_Default();//数据会保存在php.ini中xhprof.output_dir设置的目录去中 $runId = $xhprofRuns->save_run($xhprofData, 'xhprof_test');//第二个参数是定义文件名称 echo 'http://ip/xhprof/xhprof_html/index.php?run=' . $runId . '&source=xhprof_test'; ``` 变量`$runId`是本次请求生成分析结果的id,最后我们访问http://ip 页面输出了一个链接地址,使用该地址就可以看到本次请求的分析结果。 ![本次请求的分析结果](http://flt-pan.58heshihu.com/blog/typecho/lpb6vj7d.png) 注意到中间的`View Full Callgraph`链接,通过该链接我们可以看到图形化的分析结果。 ![图形化分析结果](http://flt-pan.58heshihu.com/blog/typecho/lpb6x3tt.png) #### 查看安装结果 ```bash [root@xxx /]# php -i | grep xhprof xhprof xhprof support => enabled xhprof.collect_additional_info => 0 => 0 xhprof.output_dir => /tmp => /tmp xhprof.sampling_depth => 2147483647 => 2147483647 xhprof.sampling_interval => 100000 => 100000 ``` #### 查看分析结果文件 ```bash [root@xxx /]# tree /tmp /tmp └── systemd-private-be08ad52244846b793f9a5c4600bb4dc-php-fpm.service-UXT6oP └── tmp ├── 655f4470f21de.xhprof_test.xhprof └── 655f48cb6e9bf.xhprof_test.xhprof [root@xxx /]# cat /tmp/systemd-private-be08ad52244846b793f9a5c4600bb4dc-php-fpm.service-UXT6oP/tmp/655f4470f21de.xhprof_test.xhprof a:1:{s:6:"main()";a:4:{s:2:"ct";i:1;s:2:"wt";i:889;s:2:"mu";i:61200;s:3:"pmu";i:25216;}} ``` 得到的是serialize后的数据 ```php <?php $str = 'a:1:{s:6:"main()";a:4:{s:2:"ct";i:1;s:2:"wt";i:889;s:2:"mu";i:61200;s:3:"pmu";i:25216;}}'; $arr = unserialize($str); var_dump($arr); array(1) { ["main()"]=> array(4) { ["ct"]=> int(1) ["wt"]=> int(889) ["mu"]=> int(61200) ["pmu"]=> int(25216) } } ``` 最后修改:2023 年 11 月 27 日 © 允许规范转载 赞 如果觉得我的文章对你有用,请随意赞赏