使用XHProf查找PHP性能瓶頸
XHProf是facebook 開發的一個測試php性能的擴展,本文記錄了在PHP應用中使用XHProf對PHP進行性能優化,查找性能瓶頸的方法。
下載
網上很多是編譯安裝xhprof-0.9.4版本,應該是用php5,在php8.0下編譯xhprof-0.9.4各種報錯(編譯安裝這個拓展 tideways 不會報錯),php7、php8環境下,建議下載最新版本
https://pecl.php.net/package/xhprof
安裝Xhprof擴展
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
[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
,其他函數都是不需要提供參數的,而該函數則可以接受兩個可選的參數,用于改變該工具的行為。
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
函數的分析。
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
工具,否則在生成監控圖表的時候回出現以下錯誤:
failed to execute cmd: " dot -Tpng". stderr: `sh: dot: command not found '
這里提示找不到dot
命令,所以需要先安裝graphviz
yum -y install graphviz
將xhprof安裝包中的xhprof_html_和xhproflib目錄放到服務器的web目錄下
由于分析結果的查看工具是基于web的,因此,我們需要將xhprof安裝包中的xhprof_html_和xhproflib目錄放到服務器的web目錄下,讓xhprof_html目錄中的內容對外可以訪問。
比如我的測試服務器環境是使用vagrant搭建的CentOS,這兩個目錄放到/opt/xhprof-2.3.9目錄下:
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
中的配置如下:
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 頁面頂部加上:
xhprof_enable(XHPROF_FLAGS_NO_BUILTINS + XHPROF_FLAGS_MEMORY);
在頁面底部加上:
$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://訪問http://ip 頁面輸出了一個鏈接地址,使用該地址就可以看到本次請求的分析結果。
注意到中間的View Full Callgraph
鏈接,通過該鏈接我們可以看到圖形化的分析結果。
查看安裝結果
[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
查看分析結果文件
[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
$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)}
}