XHProf:PHP 性能分析利器,快速定位代码瓶颈

小岛来信
小岛来信
发布时间:2026-08-25 | 82人浏览过
原创

前言

很多 PHP 项目一开始并不会把性能放在首位,但当页面明显变慢、接口发卡、排查又总是停留在“机器正常、数据库正常”时,就说明该把视角转到代码执行过程了。本文以 XHProf 为例,从安装依赖、运行验证到项目接入和结果解读依次展开,帮助你判断问题究竟出在调用链、函数自身,还是高频执行与内存占用。

很多 PHP 项目并不是一开始就要拼极致性能,但当页面已经出现明显变慢、请求发卡、接口响应不稳定时,只靠“加机器、扩内存”通常只能暂时缓解。要判断问题到底出在数据库、函数调用链,还是某段业务代码本身,更直接的办法是先做一次性能分析,再决定优化方向。

XHProf 是 Facebook 推出的一个被动分析器,适合用来追踪 PHP 程序的执行耗时、CPU 消耗和内存使用情况,也能在生产环境使用。下面按“什么时候该分析、怎么安装、如何接入项目、结果怎么看”这条线梳理,方便你快速上手并定位代码瓶颈。

什么时候该做性能分析

开发中经常会遇到一种情况:CPU 正常、内存正常、数据库看上去也没异常,但网站打开还是慢,接口调用还是卡。这类问题如果只靠经验排查,往往要花很多时间,而且不一定能抓到真正的热点。

在硬件成本越来越低的情况下,很多团队会优先选择“空间换时间”,比如 8G 不够就换 16G,一台机器扛不住就再加两台。这种办法当然有效,但它解决的是承载能力,不一定解决代码路径本身的低效问题。

如果你已经开始怀疑瓶颈在代码层,而不是单纯的资源不足,那么性能分析就值得尽快做。PHP 生态里常见的工具包括 xhprof、tideways、xdebug 等,而 XHProf 的优势在于接入相对直接,输出结果也足够适合做第一轮定位。

安装 XHProf 与运行依赖

文中以 PHP 7 为例安装 XHProf 扩展。先通过 Git 拉取源码,并切换到扩展目录:

暖色手绘专栏图,展示 XHProf 从扩展安装到绘图依赖检查的关键环节
XHProf 安装链路一图看懂先确认扩展、输出目录和绘图依赖,能减少后续分析页面打不开或调用图报错的问题。
git clone https://github.com/longxinH/xhprof.git ./xhprof
cd xhprof/extension/

如果你的环境是 php5.*,则需要使用另一个仓库:

https://github.com/phacility/xhprof.git

接着编译扩展,phpize 可以在 PHP 的安装目录中找到:

/usr/local/php/bin/phpize
./configure --with-php-config=/usr/local/php/bin/php-config
make && sudo make install

安装完成后,编辑 PHP 配置文件,在末尾追加:

[xhprof]
extension = xhprof.so
xhprof.output_dir = /tmp/xhprof

这里的 xhprof.output_dir 是性能分析文件的输出目录,PHP 运行用户必须对这个目录有写权限,例如:

chown -R www:www /tmp/xhprof

还有一个容易忽略的点:生成调用图时会用到 proc_open 函数,如果当前环境禁用了它,需要先开启。

修改完配置后,记得重启 php-fpm,然后用下面的命令确认扩展是否生效:

php -m |grep xhprof

除了扩展本身,还需要安装绘图依赖 graphviz,否则后面的调用图无法正常生成:

yum install libpng
yum install graphviz

如何运行并查看分析结果

在刚刚克隆下来的 XHProf 包中,自带了一个 example 示例。将整个 xhprof 目录放到一个站点目录下后,就可以先用官方示例验证环境是否跑通。

暖色手绘专栏图,展示 XHProf 结果页中调用链、红色热点和关键耗时判断方式
从结果页定位最慢调用链看到结果页后,重点不是把所有字段都读一遍,而是先找到最慢的那条调用链。

运行后,你会看到类似下面的输出:

Array
(
    [foo==>bar] => Array
        (
            [ct] => 5
            [wt] => 29
        )
、、、、

)
---------------
Assuming you have set up the http based UI for 
XHProf at some address, you can view run at 
http://<xhprof-ui-address>/index.php?run=5b6116ddebeee&source=xhprof_foo
---------------

关键是复制其中的查询参数,比如 ?run=5b6116ddebeee&source=xhprof_foo,然后带到 XHProf 的 UI 页面中打开,对应的一次分析结果就能看到。

进入结果页后,点击 [View Full Callgraph],可以查看函数调用关系和各自运行时长。通常图中被标成红色的位置,就是耗时最明显的部分,排查和优化时可以优先盯住这些函数链路。

如果页面报错:

failed to execute cmd " dot -Tpng"

一般先检查两件事:一是 proc_open 是否可用,二是 graphviz 是否已经正确安装。

此外,直接访问下面这个入口,也可以查看全部分析记录:

http://您的域名/xhprof/xhprof_html/index.php

在自己的项目里接入 XHProf

如果你不只是想跑示例,而是要在自己的项目里做真实分析,可以参考 xhprof/examples/sample.php。核心思路并不复杂:把库文件带进项目,在程序入口开启分析,在请求结束时保存结果。

首先,把 xhprof/xhprof_lib 目录拷贝到自己的项目中。

然后在程序入口文件开头加入:

xhprof_disable();

在结尾加入保存分析结果的代码:

$XHPROF_ROOT = realpath(dirname(__FILE__) .'/..');
include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";
include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";

// save raw data for this profiler run using default
// implementation of iXHProfRuns.
$xhprof_runs = new XHProfRuns_Default();

// save the run under a namespace "xhprof_foo"
$run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_foo");

程序运行一次后,就可以通过下面的地址查看结果:

http://您的域/xhprof/xhprof_html/index.php

实际使用时,更重要的不是“接入成功”本身,而是让分析发生在你真正怀疑有性能问题的请求上,例如首页、列表页、复杂报表接口或某些批处理逻辑。这样拿到的数据才更有参考价值。

结果字段怎么读,哪些指标最值得先看

XHProf 的结果页字段很多,但并不需要一上来全部吃透。先抓住“函数名、调用次数、总耗时、自身耗时、CPU、内存”这几组核心指标,就已经足够做第一轮判断。

暖色手绘专栏图,展示 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.PeakMemUse峰值百分比。

如果你只是想快速定位瓶颈,可以先这样理解:

先看总耗时,再区分是不是子调用拖慢

Incl.Wall Time 代表一个方法连同它调用的子方法一起,总共花了多少时间;Excl. Wall Time 则只看这个方法自身。前者高,说明这条链路整体慢;后者高,说明这个方法本身就重。

调用次数高,不一定是问题小

Calls 和 Calls% 可以帮助你识别“看似单次不慢,但被高频调用后累计很重”的函数。很多循环里的小函数、重复格式化、重复数据转换,都会在这里暴露出来。

CPU 和内存指标适合辅助判断瓶颈类型

如果 CPU 时间明显高,问题可能更偏计算或代码执行本身;如果 MemUse、PeakMemUse 明显异常,就要留意大数组、重复加载、对象堆积这类内存问题。

综合来看,XHProf 最有价值的地方,不是给出一个“优化建议”,而是把请求过程拆成可量化的函数链路,让你知道时间究竟消耗在了哪里。只有先找到真正的热点,再决定是改 SQL、减调用、做缓存,还是重构业务逻辑,优化才不会走偏。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表