前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >XHProf - PHP性能分析工具

XHProf - PHP性能分析工具

原创
作者头像
:Darwin
修改2023-08-19 14:01:12
2820
修改2023-08-19 14:01:12
举报
文章被收录于专栏:WorkLogsWorkLogs

本文主要介绍:

  1. 在 CentOS7 中的安装 XHProf
  2. 配置 XHProf 的网页图形化界面
  3. XHProf 数据的获取和分析
  4. 优化举例
  5. XHProf 的实现原理
  6. 其他功能

简介

XHProf是一个轻量级的 PHP 性能分析工具,提供了图形化的界面展示性能参数和过程。

1. 在 CentOS7 中的安装 XHProf

代码语言:txt
复制
#获取源码包
wget http://pecl.php.net/get/xhprof-2.3.8.tgz

tar xvf xhprof-2.3.8.tgz
cd xhprof/extension

#编译安装
/opt/remi/php74/root/usr/bin/phpize

./configure --with-php-config=/opt/remi/php74/root/usr/bin/php-config

make && make install
代码语言:txt
复制
#配置 xhprof 的扩展及运行数据路径

vim /etc/opt/remi/php74/php.d/20-xhprof.ini

; Enable xhprof extension module
extension=xhprof.so

#储存 XHProf 运行数据的默认目录,默认值是 /tmp
xhprof.output_dir=/tmp/xhprof

#重启 php-fpm
systemctl restart php74-php-fpm

#检查是否安装成功
php -m | grep xhprof

2. 配置 XHProf 的网页图形化界面

代码语言:txt
复制
# 将源码包中的lib和html文件夹复制到项目路径下
cp -r /root/xhprof-2.3.8/xhprof_html /wwwroot/xhprof/

cp -r /root/xhprof-2.3.8/xhprof_lib /wwwroot/xhprof/
代码语言:txt
复制
#配置Nginx
server {
	listen 1025;
	server_name your_ip_or_domain;
	
	root /wwwroot/xhprof/xhprof_html;
	index index.php index.html index.htm;
	
	access_log /var/log/nginx/xhprof_access.log;
	error_log /var/log/nginx/xhprof_error.log;
	
	location ~ \.php$ {
		fastcgi_pass 127.0.0.1:9000;
		fastcgi_index index.php;
		fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
		include fastcgi.conf;
	}
}

#重新加载nginx配置
nginx -s reload

#访问你的IP或域名
2.1 可能的问题
2.1.1 failed to execute cmd \" dot -Tpng\"
代码语言:txt
复制
#安装libpng
wget https://sourceforge.net/projects/libpng/files/libpng16/1.6.28/libpng-1.6.28.tar.gz/download
mv download libpng-1.6.28.tar.gz
tar -zxvf libpng-1.6.28.tar.gz
cd libpng-1.6.28
./configure 
make && make install

#安装Graphviz
wget http://www.graphviz.org/pub/graphviz/stable/SOURCES/graphviz-2.24.0.tar.gz 
tar -zxvf graphviz-2.24.0.tar.gz
cd graphviz-2.24.0
./configure
make && make install

#重启php-fpm
2.1.2 看不到数据文件
代码语言:txt
复制
#确认配置目录 xhprof.output_dir 的权限,并且存在数据文件
chown -R www:www /tmp/xhprof

3. XHProf 数据的获取和分析

3.1 数据获取
代码语言:txt
复制
#开启xhprof
xhprof_enable(XHPROF_FLAGS_NO_BUILTINS | XHPROF_FLAGS_CPU | XHPROF_FLAGS_MEMORY, []);

#停止性能分析,并返回此次运行的 xhprof 数据
$xhprof_data = xhprof_disable();

#将数据写入文件中
file_put_contents('/tmp/xhprof/' . uniqid() . '.xhprof', serialize($data));
3.2 数据分析
代码语言:txt
复制
Incl.       表示Including(包含)的缩写
Excl.       表示Excluding(不包含)的缩写
Function Name:      函数名
Calls:              调用次数
Calls%:             调用次数的百分比
Incl. Wall Time:    包含子函数执行的所有花费时间。单位:微秒(下同)
Excl. Wall Time:    函数本身执行所花费的时间。
Incl. CPU:          包含子函数执行的所花费的CPU时间。
Excl. CPU:          函数本身执行所花费的CPU时间。
Incl.MemUse:        包含子函数执行的所占用的内存。单位:字节(下同)
Excl.MemUse:        函数本身执行所占用的内存。
Incl.PeakMemUse:    包含子函数执行,所占用内存的峰值。
Excl.PeakMemUse:    函数本身执行所占用内存的峰值。

即:

  • 包含子函数的程序执行时间、CPU时间和内存消耗
  • 函数本身的程序执行时间、CPU时间和内存消耗
  • 所占程序执行时间、CPU时间或者内存消耗的百分比

4. 优化举例

  • 找一个较慢的API的运行数据,打开执行报告(Run Report),按照函数本身执行时间(Excl. Wall Time),倒序排序。
  • 可以看到排在上面的就是占用时间最多的函数,点击函数名称进入该函数的调用关系表中,往父级函数(Parent function)或子级函数(Parent function)找到自己开发的函数。
  • 查看函数代码,找出可优化点,比如循环查数据库是否可改为批量查,sql 是否使用了索引等等。
  • 实施代码优化方案,比如代码结构,数据缓存,MySQL索引
  • 同理,根据内存排序,可以找到是否使用了 select * from table_name 这样的查询语句,改成获取具体的字段,也可以用Redis,ES等手段去优化。

5. XHProf 的实现原理

5.1 PHP 是如何加载第三方扩展的?

加载流程:

扩展会提供一个 get_module(void) 的方法拿到扩展的 zend_module_entry 结构体的定义 扩展被编译成so文件后,在php.ini文件中配置 xxx.so, 表示加载扩展 php 启动的时候会读取 php.ini 文件,并做解析 在 Linux 下 通过 dlopen() 打开扩展的 xxx.so 库文件 通过系统的 dlsym() 获取动态库中 get_module() 函数的地址,执行每个扩展的 get_module 方法拿到 zend_module_entry 结构体 把zend_module_entry 结构体注册到php的 extension_lists 扩展列表中。extension_lists是一个链表,保存着根据php.ini中定义的extension=xxx.so取到的全部扩展名称,其中engine是zend扩展,functions为php扩展在 php 的生命周期中执行各个扩展定义的 PHP_MINIT

5.2 如何收集性能数据的?

起始外部扩展就是相当于内核一个模块,都是 zend_module_entry 结构体。

收集性能数据的原理,就是在模块初始化的时候代理了这个,代理了这个编译和执行OPCODE 的函数,覆盖了一层,加了自己的处理,自己的处理就是C代码的执行时间和PHP申请堆内存的计算。

收集数据的过程其实针对函数嵌套调用递归收集的过程。在调用xhprof_enable方法时,会把默认的方法替换为xhprof的方法。

代码语言:C
复制
static void hp_begin(long level, long xhprof_flags)
{
    if (!hp_globals.enabled)
    {
        int hp_profile_flag = 1;

        hp_globals.enabled = 1;
        hp_globals.xhprof_flags = (uint32) xhprof_flags;

         /* Replace zend_compile with our proxy */
                 /* 处理加载PHP文件 */
                 /* 先把zend引擎默认处理方法保存到_zend_compile_file变量中。*/
        _zend_compile_file = zend_compile_file;
                /* 在把xhprof相对应的方法赋值给zend_compile_file。
                   这样,每次加载PHP文件时,就会执行xhprof相应的方法。*/
        zend_compile_file  = hp_compile_file;

        /* Replace zend_compile_string with our proxy */
                /* 处理eval代码的执行 */
        _zend_compile_string = zend_compile_string;
        zend_compile_string  = hp_compile_string;

        /*init the execute pointer*/
                /* 处理 函数方法的执行 */
        _zend_execute_ex = zend_execute_ex;
        zend_execute_ex  = hp_execute_ex;
	    
	    // .........
    }
}

/*那我们看下,hp_compile_file方法,又是如何实现的*/
ZEND_DLEXPORT zend_op_array* hp_compile_file(zend_file_handle *file_handle, int type)
{
    const char *filename;
    char *func;
    int len;
    zend_op_array *ret;
    int hp_profile_flag = 1;

    filename = hp_get_base_filename(file_handle->filename);
    len = sizeof("load") - 1 + strlen(filename) + 3;
    func = (char *) emalloc(len);
    snprintf(func, len, "load::%s", filename);
        
        //方法执行前记录当前各项性能如数,如cpu 内存等
    BEGIN_PROFILING(&hp_globals.entries, func, hp_profile_flag);
        //开始zend引擎相应的方法,加载文件
    ret = _zend_compile_file(file_handle, type);
    if (hp_globals.entries)
    {
                //加载文件完毕后,再次记录当前各项性能数据。以便以后计算差值。
        END_PROFILING(&hp_globals.entries, hp_profile_flag);
    }

    efree(func);
    return ret;

xhprof_enable开启的这一行解析成 OPCODE,然后执行之前代理的 hp_execute_ex 函数,初始化main节点,来存当前调用PHP函数的性能数据 ct wt mu pmu等数据。

在xhprof_enable 和  xhprof_disable 之前的PHP代码,每一行都会被翻译成OPCODE,都会执行hp_execute_ex 函数。

$xhprof_data = xhprof_disable();

结束收集数据,返回已经收集到的数据。

数据格式在内核里面是一个二级HASHTABLE,对应PHP是一个二维数组

这个结束过程是对应这之前xhprof_enable的执行过程,是对main虚拟的节点做回源处理。

xhprof_enable提供了三个常量,用于设置你是否需要统计PHP内置函数,都统计那些指标。

三个常量如下:

代码语言:txt
复制
XHPROF_FLAGS_NO_BUILTINS
设置这个常量后,将不统计PHP内置函数。毕竟PHP的内置函数性能一般都不错。没必要再消耗性能去统计。所以,建议设置。

XHPROF_FLAGS_CPU
设置这个常量后,会统计进程占用CPU时间。由于CPU时间是通过调用系统调用getrusage获取,导致性能比较差。开启这个选项后,大概性能下降一半。因此,如果对cpu耗时不是特别敏感的情况下,建议不要启用这个选项。

XHPROF_FLAGS_MEMORY
设置这个常量后,将会统计内存占用情况。由于获取内存情况,使用的是zend_memory_usage和zend_memory_peak_usage,并不是系统调用。因此,对性能影响不大。如果需要对内存使用情况进行分析的情况下,可以开启。

6. 其他功能

非侵入式监控php应用性能

xhprof_lib/utils/xhprof_lib.php文件包含额外的库函数,可用于维护、汇总XHProf运行结果。

例如:

  • xhprof_aggregate_runs() :可用于多次XHProf运行结果汇总到一个单一的运行。这可以帮助您使用XHProf来建立一个全系统“的函数级别”的性能监测工具 。 例如,您可以在生产环境中定期抽样XHProf的数据,产生小时/日 级别的报告。
  • xhprof_prune_run() :汇总大量XHProf运行结果(特别是如果它们对应不同类型的程序)将可能导致callgraph规模变得太大。您可以使用xhprof_prune_run功 能来修剪的callgraph数据中只占总运行时间中很小比例的子树。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 简介
  • 1. 在 CentOS7 中的安装 XHProf
  • 2. 配置 XHProf 的网页图形化界面
    • 2.1 可能的问题
      • 2.1.1 failed to execute cmd \" dot -Tpng\"
      • 2.1.2 看不到数据文件
  • 3. XHProf 数据的获取和分析
    • 3.1 数据获取
      • 3.2 数据分析
      • 4. 优化举例
      • 5. XHProf 的实现原理
        • 5.1 PHP 是如何加载第三方扩展的?
          • 5.2 如何收集性能数据的?
          • 6. 其他功能
          相关产品与服务
          云数据库 MySQL
          腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档