XHProf

目录

  1. 导言
  2. XHProf 概况
  3. 安装XHProf扩展
  4. 使用XHProf进行性能分析
  5. 设置XHProf用户界面
  6. 在生产环境中使用XHProf注意事项
  7. 轻量级采样模式
  8. 附加功能
  9. 信赖
  10. 鸣谢
  1. 导言

    XHProf是一个分层PHP性能分析工具。它报告函数级别的请求次数和各种指标,包括阻塞时间,CPU时间和内存使用情况。一个函数的开销,可细分成调用者和被调用者的开销。原始数据收集部分是用纯C实现的,是一个名叫xhprof的 Zend扩展 。XHProf有一个简单的HTML的用户界面( PHP写成的)。基于浏览器的性能分析用户界面能更容易查看,或是与同行们分享成果。也能绘制调用关系图。

    XHProf报告往往可以有助于理解被执行的代码的结构。的等级性质的报告可用来确定,例如,什么链要求导致了所谓的特殊功能得到。

    XHProf可以比较两次运行的结果(又名"差异报告")或是从多次运行得到的汇总数据。差异和汇总报告,就像单一运行报告一样,也提供"平板"以及"分层"的性能分析视图。

    XHProf是一种轻量级的性能分析工具。在数据收集阶段,它记录调用次数的追踪和包容性的指标弧在动态callgraph的一个程序。它独有的数据计算的报告/后处理阶段。在数据收集时,XHProfd通过检测循环来处理递归的函数调用,并通过给递归调用中每个深度的调用一个有用的命名来避开死循环。

    XHProf的轻量级性质和汇聚功能,使得它非常适合用于收集"生产环境"的性能统计数据的统计。[见用于生产环境的补充说明。 ]


    例如,XHProfLive (不属于开源的工具包),是Facebook使用的一个全系统的性能监测系统,建立在XHProf的基础之上 。 XHProfLive不断收集函数级别的Profiler资料,这些资料来自生产环境中运行中的示例页面[用xhprof来收集] 。然后XHProfLive汇总指定的时候,页面类型,来取得各个页面的性能数据,可以帮助解决各种问题,如:特定的某个网页的函数级的性能情况怎样?在所有网页中,或者对某一具体的网页,foo 函数的开销如何?在过去的一小时/天/周中,哪些函数返回的次数最多?某个页面/函数的执行时间的变化趋势如何?等等。


    由Facebook最初开发的XHProf在2009年3月开放源代码。

  2. XHProf概况

    XHProf提供的功能:

  3. 安装XHProf扩展

    这个扩展在"extension"子目录里。


    注:Windows的版本还没有实现。目前我们已经在Linux/FreeBSD 上测试了xhprof。

    0.9.2及以上版本的XHProf也正计划着移植到Mac OS下。[我们在Mac OS 10.5下测试过。]

    注: XHProf使用RDTSC指令(时间戳计数器)来实现一个真正的低资源消耗的计数器[针对elapsed time]。因此目前xhprof还只适用于x86架构。此外,因为RDTSC的数据不能在CPU间同步,在剖析时xhprof会将程序绑定在单个的CPU。

    如果SpeedStep技术是打开的,XHProf的基于RDTSC定时器的功能就不能正常工作了。这项技术在某些英特尔处理器上是可用的。[注:苹果台式机和笔记本电脑一般都将SpeedStep技术预设开启。使用XHProf,您需要禁用SpeedStep技术。 ]


    下面的步骤,应该在Linux / Unix环境下进行。

    % cd <xhprof_source_directory>/extension/
    % phpize
    % ./configure --with-php-config=<path to php-config >
    % make
    % make install
    % make test
    

    php.ini文件 :您可以更新您的php.ini文件来自动加载您的扩展。将以下内容添加到你的php.ini文件。

    [xhprof]
    extension=xhprof.so
    ;
    ; directory used by default implementation of the iXHProfRuns
    ; interface (namely, the XHProfRuns_Default class) for storing
    ; XHProf runs.
    ;
    xhprof.output_dir=<directory_for_storing_xhprof_runs >
    
  4.   使用XHProf来做性能分析

    用下面的示例程序来试着产生一些原始的性能数据:

    foo.php

    <?php
    
    function bar($x) {
      if ($x > 0) {
        bar($x - 1);
      }
    }
    
    function foo() {
      for ($idx = 0; $idx < 2; $idx++ ) {
        bar($idx);
        $x = strlen("abc");
      }
    }
    
    // start profiling
    xhprof_enable();
    
    // run program
    foo();
    
    // stop profiler
    $xhprof_data = xhprof_disable();
    
    // display raw xhprof data for the profiler run
    print_r($xhprof_data);
    
    
    
    

    运行上面的测试程序:

    % php -dextension=xhprof.so foo.php

    你应该得到的输出如下:

    Array
    (
        [foo==>bar] => Array
            (
                [ct] => 2         # 2 calls to bar() from foo()
                [wt] => 27        # inclusive time in bar() when called from foo()
            )
    
        [foo==>strlen] => Array
            (
                [ct] => 2
                [wt] => 2
            )
    
        [bar==>bar@1] => Array    # a recursive call to bar()
            (
                [ct] => 1
                [wt] => 2
            )
    
        [main()==>foo] => Array
            (
                [ct] => 1
                [wt] => 74
            )
    
        [main()==>xhprof_disable] => Array
            (
                [ct] => 1
                [wt] => 0
            )
    
        [main()] => Array         # fake symbol representing root
            (
                [ct] => 1
                [wt] => 83
            )
    
    )
    

    注:The raw data only contains "inclusive" metrics. For example, the wall time metric in the raw data represents inclusive time in microsecs. Exclusive times for any function are computed during the analysis/reporting phase.

    注意:默认情况下只有调用次数和占用时间是记录了的。您也可以选择同时记录CPU时间和/或单纯记录内存使用量。将下面这一行:

    xhprof_enable();
    换成:
    xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY); ;

    您应该得到如下的输出:

    Array
    (
        [foo==>bar] => Array
            (
                [ct] => 2        # number of calls to bar() from foo()
                [wt] => 37       # time in bar() when called from foo()
                [cpu] => 0       # cpu time in bar() when called from foo()
                [mu] => 2208     # change in PHP memory usage in bar() when called from foo()
                [pmu] => 0       # change in PHP peak memory usage in bar() when called from foo()
            )
    
        [foo==>strlen] => Array
            (
                [ct] => 2
                [wt] => 3
                [cpu] => 0
                [mu] => 624
                [pmu] => 0
            )
    
        [bar==>bar@1] => Array
            (
                [ct] => 1
                [wt] => 2
                [cpu] => 0
                [mu] => 856
                [pmu] => 0
            )
    
        [main()==>foo] => Array
            (
                [ct] => 1
                [wt] => 104
                [cpu] => 0
                [mu] => 4168
                [pmu] => 0
            )
    
        [main()==>xhprof_disable] => Array
            (
                [ct] => 1
                [wt] => 1
                [cpu] => 0
                [mu] => 344
                [pmu] => 0
            )
    
        [main()] => Array
            (
                [ct] => 1
                [wt] => 139
                [cpu] => 0
                [mu] => 5936
                [pmu] => 0
            )
    
    )
    

    在分析时跳过内置函数

    默认情况下, PHP的内置函数(如strlen )也被分析了。如果您不希望分析内置函数(为了进一步减少性能分析的开销貌相或减小产生的原始数据) ,您可以使用XHPROF_FLAGS_NO_BUILTINS标 志,例如:

    // do not profile builtin functionsxhprof_enable ( XHPROF_FLAGS_NO_BUILTINS ) ;

    在性能分析时忽略特定函数( 0.9.2或更高版本支持)

    从0.9.2版本的xhprof ,你可以告诉XHProf在性能分析时忽略一些指定函数。这样,您就可以忽略,像功能用于间接函数调用,如call_user_funccall_user_func_array的函数 。这些不必要的中间层,因为他们乱七八糟的父-子关系和间接的互相调用,使XHProf报告难以理解。

    要设定要忽略的函数列表,可以在分析时给xhprof_enable函数 指定第二个参数[是个可选参数] 。例如,

    
    // elapsed time profiling; ignore call_user_func* during profiling
    xhprof_enable(0,
                 array('ignored_functions' =>  array('call_user_func',
                                                     'call_user_func_array')));
    
    or,
    
    // elapsed time + memory profiling; ignore call_user_func* during profiling
    xhprof_enable(XHPROF_FLAGS_MEMORY,
                  array('ignored_functions' => array('call_user_func',
                                                      'call_user_func_array')));
    
    
    
    
  5. 设置XHProf用户界面

    1. PHP源码目录结构

      XHProf的用户界面的由PHP实现。代码存放在两个子目录中: xhprof_html/xhprof_lib/

      xhprof_html目录包含了3个顶级PHP页面。

      • index.php :查看一个单一运行或差异报告。
      • callgraph.php :以图片的形式查看一次XHProf运行的调用关系图。
      • typeahead.php :在XHProf的报告中被后台调用来进行函数的自动补全。

      xhprof_lib目录包含进行分析和显示的支持代码(计算 扁平的性能信息,计算diffs,从多次运行中汇总数据等等) 。

    2. Web服务器配置:您需要确保您的Web服务器有权限读取xhprof_html/目录可,并且能运行PHP脚本。

    3. 管理XHProf运行

      客户端能很灵活地保存他们从XHProf运行中得到的XHProf原始数据。用户界面层的XHProf提供了一个 iXHProfRuns接口(见xhprof_lib / utils / xhprof_runs.php ),客户端可以利用。这使得客户端能够告诉用户界面层,如何获取XHProf运行后产生的对应数据。

      XHProf的UI 库自带的有一个基于文件的iXHProfRUns接口实现,即" XHProfRuns_Default"(见xhprof_lib/utils/xhprof_runs.php)。这个自带的实现将XHProf运行结果存在ini配置的xhprof.output_dir参数指定的某个目录下。

      一次XHProf运行,必须用一个命名空间和运行编号来唯一确定。

      一)XHProf数据的永久保存

      假设您使用的iXHProfRuns接口的XHProfRuns_Default这个默认实现方式,一个典型的XHProf运行并保存结果的操作可能就是这样子的:

      // start profiling
      xhprof_enable();
      
      // run program
      ....
      
      // stop profiler
      $xhprof_data = xhprof_disable();
      
      //
      // Saving the XHProf run
      // using the default implementation of iXHProfRuns.
      //
      include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_lib.php";
      include_once $XHPROF_ROOT . "/xhprof_lib/utils/xhprof_runs.php";
      
      $xhprof_runs = new XHProfRuns_Default();
      
      // Save the run under a namespace "xhprof_foo".
      //
      // **NOTE**:
      // By default save_run() will automatically generate a unique
      // run id for you. [You can override that behavior by passing
      // a run id (optional arg) to the save_run() method instead.]
      //
      $run_id = $xhprof_runs->save_run($xhprof_data, "xhprof_foo");
      
      echo "---------------\n".
           "Assuming you have set up the http based UI for \n".
           "XHProf at some address, you can view run at \n".
           "http://<xhprof-ui-address >/index.php?run=$run_id&source=xhprof_foo\n".
           "---------------\n";
      
      

      上述代码就会将运行结果保存在xhprof.output_dir ini参数指定的特定目录下。文件的名称可能会是49bafaa3a3f66.xhprof_foo类似的东东 ;两部分分别是运行编号( " 49bafaa3a3f66 " )和命名空间( " xhprof_foo " )。[如果您想创建/指定自己的run ID (如数据库中的序列号,或时间戳) ,您可以通过明确的给save_run方法指定一个run id。

      b )使用自己实现的iXHProfRuns

      如果您希望您的XHProf运行存储在不同的数据库(例如用一个压缩格式,或者其他什么地方比如数据库等),您需实现一个类,这个实必须实现iXHProfRuns ( )接口。

      您还需要修改"xhprof_html/"目录中3个主要的PHP入口文件(index.php , callgraph.php , typeahead.php ),使用新的类而不是默认的类XHProfRuns_Default 。改变3个文件的这一行:

      $xhprof_runs_impl = new XHProfRuns_Default();

      您还需要"include"声明了上述class的文件。

    4. 从用户界面中访问各次运行

      一)看单一运行报告

      要查看run id是<run_id>和命名空间是<namespace>的报告,访问URL:

      http://<xhprof-ui-address>/index.php?run=<run_id>&source=<namespace>

      例如,

      http://<xhprof-ui-address>/index.php?run=49bafaa3a3f66&source=xhprof_foo

      二)查看diff报告

      要查看命名空间<namespace>下runid分别是< run_id1>和<run_id2>的两个报告,访问URL:

      http://<xhprof-ui-address>/index.php?run1=<run_id1>&run2=<run_id2>&source=<namespace>

      三)汇总报告

      您也可以指定一组run id来汇总得到您想要的报告视图。

      如果你有三个XHProf运行,都在"benchmark'命名空间下,run id分别是1,2,3。要查看这些运行的汇总报告:

      http://<xhprof-ui-address>/index.php?run=1,2,3&source=benchmark

      加权汇总 :进一步假设,上述3个运特分别对应三种程序,p1.php,p2.php和p3.php ,通常以20%,30%,50%概率混合:要查看汇总报告所对应的加权平均数这些运行使用:

      http://<xhprof-ui-address>/index.php?run=1,2,3&wts=20,30,50&source=benchmark

  6. 在生产环境中使用XHProf的注意事 项

    这里给出一些意见和准则。您的情况可能会有所不同:

  7. 轻量级采样模式

    Xhprof扩展还提供了一个轻量级采样模式 。采样间隔为0.1秒。采样记录了整个函数调用堆栈。如果想以增加极低的负载作代价来进行性能监控和诊断,采样模式就是你想要的。

    XHProf扩展提供的与采样模式相关的函数有xhprof_sample_enable()xhprof_sample_disable()

    [ 待定 :关于采样模式更详细的文档。 ]

  8. 附加功能

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

    例如:

  9. 依赖性