在PHP中分析超过函数调用

时间:2011-03-26 16:10:07

标签: php performance profiling

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

虽然这本质上是正确的,但是当我尝试优化代码时,它没有那么多帮助。例如,我有一个带有两个if语句的简单函数,它返回一个带有两个值的数组,根据xdebug,它占总执行时间的26%。它使用array_key_exists,我可以看到这些函数调用的执行时间约为1%。我看不到的是其他25%的执行时间如何与其余代码相比较。 if语句需要多长时间,获取值对,将这些值放入数组中,然后返回数组?

我在文档中遗漏了一些xdebug标志吗?或者是否有PHP的分析工具,它将分析变量赋值,运算符,基本数据类型的构造以及其他非函数调用?优选地,不假定脚本可通过Web服务器获得的东西。

我正在寻找的东西的一个例子是perl的nytprof。请注意,它描述了函数中的每一行代码(除了最后一次推送,从不执行)。

2 个答案:

答案 0 :(得分:4)

如果您想衡量每行统计信息,我建议您使用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

答案 1 :(得分:3)

当你在xdebug下运行程序时,你应该能够随机地break手动执行它,然后显示调用堆栈。 如果你多次这样做,任何负责大量时间的代码都会出现在那些堆栈样本上。这是random-pause技术。

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