【问题标题】:Profiling more than function calls in PHP在 PHP 中分析不仅仅是函数调用
【发布时间】:2011-07-23 13:06:25
【问题描述】:

我已经开始了我的第一个比较严肃的 PHP 编程项目,并且很难获得好的分析信息。我使用 xdebug 和 cachegrind 来分析代码,但它假定函数的“自身”执行时间是函数执行所需的总时间减去它调用的任何函数的总时间。

虽然这在本质上是正确的,但当我尝试优化代码时,它并没有提供尽可能多的帮助。例如,我有一个带有两个 if 语句的简单函数,它返回一个包含两个值的数组,根据 xdebug,它占用了总执行时间的 26%。它使用array_key_exists,我可以看到这些函数调用的执行时间大约是1%。我看不到其他 25% 的执行时间是如何与其余代码一起计算的。 if 语句、获取值对、将这些值放入数组并返回数组需要多长时间?

我在文档中遗漏了一些 xdebug 标志吗?或者是否有一个用于 PHP 的分析工具来分析变量分配、运算符、基本数据类型的构造和其他非功能调用?最好不要假设脚本可以通过 Web 服务器获得。

我正在寻找的一个例子是nytprof for perl。请注意,它会分析函数中的每一行代码(除了最后一次推送,它永远不会被执行)。

【问题讨论】:

    标签: php performance profiling


    【解决方案1】:

    如果您想测量每行统计信息,我建议您使用 declare(用于刻度)、register_tick_functionmicrotime()debug_backtrace 的组合。这使您可以在每行执行后调用任意函数,而无需更改 PHP 源代码。

    例如:

    declare(ticks=1);
    register_tick_function( function() {
        print "call\n";
    } );
    
    print "hi\n";
    print "bye\n";
    

    其实是这样运行的:

    declare(ticks=1);
    register_tick_function( function() {
        //print "call\n";
    } ); print "call\n";
    
    print "hi\n"; print "call\n";
    print "bye\n"; print "call\n";
    

    如果你走这条路,不幸的是你自己。您必须自己记录数据,进行分析,然后找出将其与源相关联的方法。这不是不可能的,但据我所知,PHP 尚不存在这些工具。

    概念验证:

    function _line_benchmark( $reset = FALSE ) {
        $start_time = microtime(TRUE);
        static $lastFrame = NULL;
    
        if( $reset ) {
            $lastFrame = NULL;
            return;
        }
    
        $backtrace = debug_backtrace();
        $last_backtrace = $backtrace[count($backtrace) -1];
        $file = $last_backtrace["file"];
        $line = $last_backtrace["line"];
        $source = file_get_contents( $file );
        $source_lines = preg_split( "/((\r(?!\n))|((?<!\r)\n)|(\r\n))/", $source );
    
        if( ! is_null( $lastFrame ) ) {
            if( $lastFrame["line"]+1 <= $line ) {
                print "{\n";
                for( $i = $lastFrame["line"]+1; $i <= $line; $i++ ) {
                    print "#".($i-1)."\t".$source_lines[$i-1]."\n";
                }
                print "} - ".($start_time-$lastFrame["time"])."\n\n";
            }
        }
    
        $lastFrame = array(
            "time" => microtime(TRUE),
            "file" => $file,
            "line" => $line
        );
    }
    
    function line_benchmark_start() {
        _line_benchmark( TRUE ); //reset benchmark functions
    
        declare(ticks=1);
        register_tick_function( "_line_benchmark" );
    }
    
    function line_benchmark_stop() {
        unregister_tick_function( "_line_benchmark" );
    }
    
    
    line_benchmark_start();
    
    usleep( 100 );
    usleep( 1000 );
    usleep( 10000 );
    usleep( 100000 );
    usleep( 1000000 );
    
    line_benchmark_stop();
    

    输出

    {
    #48     
    #49     usleep( 100 );
    } - 0.000154972076416
    
    {
    #50     usleep( 1000 );
    } - 0.00112199783325
    
    {
    #51     usleep( 10000 );
    } - 0.0101318359375
    
    {
    #52     usleep( 100000 );
    } - 0.0998418331146
    
    {
    #53     usleep( 1000000 );
    } - 0.999831914902
    

    【讨论】:

      【解决方案2】:

      当您在xdebug 下运行程序时,您应该能够在随机时间手动将break 放入它的执行中,然后显示调用堆栈。 如果您多次这样做,任何占用大量时间的代码都将出现在这些堆栈示例中。这就是random-pause 技术。

      这有点像大脑转变。诸如自我时间、测量函数执行时间或计数调用之类的东西,为了发现问题,充其量只是间接信息。相反,它表示任何实际花费大量时间的东西都无法逃脱您的注意,如果您只是顺便去看看并看看。任何你看到它“正在做”的事情(这意味着你在堆栈上不止一次捕获的任何代码行)如果你可以避免这样做,将会提供很好的加速。测量精度无关紧要。你已经找到了,所以你可以修复它,然后测量加速。

      【讨论】:

      • 在查看了您的建议几次并试图将我的想法围绕它(您的正确,这是一个大脑转移)之后,我认为随机暂停技术与运行 cachegrind 非常相似分析应用程序。
      • 编辑太慢:无论使用何种工具或技术,目标都是确定哪些操作需要时间,我认为这两种方法都做得很好。我一直在寻找一种让分析器提供更详细信息以更好地定义问题的方法。使用分析器获取这些信息将使代码优化更容易(无需检测)。似乎随机暂停可以做到这一点,但似乎也更加劳动密集。
      • @Ogo:你可能会感到惊讶。你有一个函数 F,xdebug 说它需要 26% 的时间,但它调用的 G 只需要 1%,所以你很困惑。好的,那么 10 个样本应该在其中 2.6 个上显示 F,并且它们还将显示 F 中的语句在那段时间内它所在的位置。所以我知道这听起来更像是调试,而不是运行一个工具并对输出感到困惑,但在安装另一个工具并阅读其手册之前,你仍然会得到答案。 Here's another take on it.
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-15
      • 1970-01-01
      • 1970-01-01
      • 2016-08-06
      • 1970-01-01
      相关资源
      最近更新 更多