在PHP中跟踪脚本执行时间

时间:2009-02-11 00:58:31

标签: php performance

PHP必须跟踪特定脚本用于强制执行max_execution_time限制的CPU时间量。

有没有办法在脚本内部访问这个?我想在我的测试中包含一些日志,记录实际PHP中有多少CPU被烧掉(当脚本处于等待数据库时,时间不会增加。)

我正在使用Linux机器。

20 个答案:

答案 0 :(得分:455)

如果您需要的只是挂钟时间而不是CPU执行时间,那么计算起来很简单:

//place this before any script you want to calculate time
$time_start = microtime(true); 

//sample script
for($i=0; $i<1000; $i++){
 //do anything
}

$time_end = microtime(true);

//dividing with 60 will give the execution time in minutes otherwise seconds
$execution_time = ($time_end - $time_start)/60;

//execution time of the script
echo '<b>Total Execution Time:</b> '.$execution_time.' Mins';
// if you get weird results, use number_format((float) $execution_time, 10) 

请注意,这将包括PHP等待外部资源(如磁盘或数据库)的时间,这些资源不用于max_execution_time。

答案 1 :(得分:203)

在unixoid系统上(以及在Windows上的php 7+中),您可以使用getrusage,例如:

// Script start
$rustart = getrusage();

// Code ...

// Script end
function rutime($ru, $rus, $index) {
    return ($ru["ru_$index.tv_sec"]*1000 + intval($ru["ru_$index.tv_usec"]/1000))
     -  ($rus["ru_$index.tv_sec"]*1000 + intval($rus["ru_$index.tv_usec"]/1000));
}

$ru = getrusage();
echo "This process used " . rutime($ru, $rustart, "utime") .
    " ms for its computations\n";
echo "It spent " . rutime($ru, $rustart, "stime") .
    " ms in system calls\n";

请注意,如果要为每个测试生成一个php实例,则无需计算差异。

答案 2 :(得分:90)

更短版本的talal7860的回答

<?php
// At start of script
$time_start = microtime(true); 

// Anywhere else in the script
echo 'Total execution time in seconds: ' . (microtime(true) - $time_start);

正如所指出的,这是'挂钟时间'而不是'cpu time'

答案 3 :(得分:69)

最简单的方法:

<?php

$time1 = microtime(true);

//script code
//...

$time2 = microtime(true);
echo 'script execution time: ' . ($time2 - $time1); //value in seconds

答案 4 :(得分:32)

<?php
// Randomize sleeping time
usleep(mt_rand(100, 10000));

// As of PHP 5.4.0, REQUEST_TIME_FLOAT is available in the $_SERVER superglobal array.
// It contains the timestamp of the start of the request with microsecond precision.
$time = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];

echo "Did nothing in $time seconds\n";
?>

答案 5 :(得分:24)

我用phihag回答创建了一个ExecutionTime类,你可以在盒子外面使用它:

class ExecutionTime
{
     private $startTime;
     private $endTime;

     public function start(){
         $this->startTime = getrusage();
     }

     public function end(){
         $this->endTime = getrusage();
     }

     private function runTime($ru, $rus, $index) {
         return ($ru["ru_$index.tv_sec"]*1000 + intval($ru["ru_$index.tv_usec"]/1000))
     -  ($rus["ru_$index.tv_sec"]*1000 + intval($rus["ru_$index.tv_usec"]/1000));
     }    

     public function __toString(){
         return "This process used " . $this->runTime($this->endTime, $this->startTime, "utime") .
        " ms for its computations\nIt spent " . $this->runTime($this->endTime, $this->startTime, "stime") .
        " ms in system calls\n";
     }
 }

用法:

$executionTime = new ExecutionTime();
$executionTime->start();
// code
$executionTime->end();
echo $executionTime;
  

注意:在PHP 5中,getrusage函数仅适用于Unix-oid系统。从PHP 7开始,它也适用于Windows。

答案 6 :(得分:11)

developerfusion.com上的Gringod给出了这个好答案:

<!-- put this at the top of the page --> 
<?php 
   $mtime = microtime(); 
   $mtime = explode(" ",$mtime); 
   $mtime = $mtime[1] + $mtime[0]; 
   $starttime = $mtime; 
;?> 

<!-- put other code and html in here -->


<!-- put this code at the bottom of the page -->
<?php 
   $mtime = microtime(); 
   $mtime = explode(" ",$mtime); 
   $mtime = $mtime[1] + $mtime[0]; 
   $endtime = $mtime; 
   $totaltime = ($endtime - $starttime); 
   echo "This page was created in ".$totaltime." seconds"; 
;?>

从(http://www.developerfusion.com/code/2058/determine-execution-time-in-php/

答案 7 :(得分:8)

我认为你应该看看xdebug。分析选项将使您了解许多与流程相关的项目。

http://www.xdebug.org/

答案 8 :(得分:8)

最简单,最脏的方法就是在代码中想要进行基准测试的位置调用microtime()。在数据库查询之前和之后立即执行此操作,并且可以从脚本执行的其余时间中删除这些持续时间。

提示:您的PHP执行时间很少会使您的脚本超时。如果脚本超时,它几乎总是调用外部资源。

PHP microtime文档: http://us.php.net/microtime

答案 9 :(得分:7)

如果您将秒输出格式化为:

,那将会更漂亮
echo "Process took ". number_format(microtime(true) - $start, 2). " seconds.";

将打印

Process took 6.45 seconds.

这比

要好得多
Process took 6.4518549156189 seconds.

答案 10 :(得分:3)

当PHP中有闭包功能时,为什么不从中受益呢?

function startTime(){
    $startTime = microtime(true);
    return function () use ($startTime){
        return microtime(true) - $startTime;
    };
}

现在借助上述功能,我们可以像这样跟踪时间

$stopTime = startTime();
//some code block or line
$elapsedTime = $stopTime();

每次调用startTime函数都会启动一个单独的时间跟踪器。因此,您可以根据需要启动任意数量的设备,并可以在任意位置停止它们。

答案 11 :(得分:3)

$_SERVER['REQUEST_TIME']

也检查一下。即

...
// your codes running
...
echo (time() - $_SERVER['REQUEST_TIME']);

答案 12 :(得分:2)

我写了一个检查剩余执行时间的函数。

警告: Windows和Linux平台上的执行时间计数不同。

/**
 * Check if more that `$miliseconds` ms remains
 * to error `PHP Fatal error:  Maximum execution time exceeded`
 * 
 * @param int $miliseconds
 * @return bool
 */
function isRemainingMaxExecutionTimeBiggerThan($miliseconds = 5000) {
    $max_execution_time = ini_get('max_execution_time');
    if ($max_execution_time === 0) {
        // No script time limitation
        return true;
    }
    if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
        // On Windows: The real time is measured.
        $spendMiliseconds = (microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"]) * 1000;
    } else {
        // On Linux: Any time spent on activity that happens outside the execution
        //           of the script such as system calls using system(), stream operations
        //           database queries, etc. is not included.
        //           @see http://php.net/manual/en/function.set-time-limit.php
        $resourceUsages = getrusage();
        $spendMiliseconds = $resourceUsages['ru_utime.tv_sec'] * 1000 + $resourceUsages['ru_utime.tv_usec'] / 1000;
    }
    $remainingMiliseconds = $max_execution_time * 1000 - $spendMiliseconds;
    return ($remainingMiliseconds >= $miliseconds);
}

使用:

while (true) {
    // so something

    if (!isRemainingMaxExecutionTimeBiggerThan(5000)) {
        // Time to die.
        // Safely close DB and done the iteration.
    }
}

答案 13 :(得分:1)

您可能只想知道部分脚本的执行时间。计算时间部分或整个脚本最灵活的方法是创建3个简单的函数(这里给出的过程代码,但你可以通过在它周围放置类计时器{}并进行一些调整将其转换为类。此代码有效,只需复制并粘贴并运行:

override func viewDidAppear(animated: Bool) {
    let vc = self.storyboard?.instantiateViewControllerWithIdentifier("LoginScreen")
    self.presentViewController(vc!, animated: true, completion: nil)
}

答案 14 :(得分:1)

在页面底部居中打印的小脚本,以微秒精度从服务器调用开始执行脚本。

为了不使结果失真并且与页面中的内容100%兼容,我习惯将结果写在浏览器端的本机javascript代码段上。

//Uncomment the line below to test with 2 seconds 
//usleep(2000000);

$prec = 5; // numbers after comma
$time = number_format(microtime(true) - $_SERVER['REQUEST_TIME_FLOAT'], $prec, '.', '');
echo "<script>
    if(!tI) { 
        var tI=document.createElement('div');
        tI.style.fontSize='8px';
        tI.style.marginBottom='5px';
        tI.style.position='absolute';
        tI.style.bottom='0px';
        tI.style.textAlign='center';
        tI.style.width='98%';
        document.body.appendChild(tI);
    }
    tI.innerHTML='$time';
</script>";

另一种方法是使代码段尽可能小,并在样式表中使用类对其进行样式设置。

  1. echo ...;部分替换为以下内容:

    echo "<script>if(!tI){var tI=document.createElement('div');tI.className='ldtme';document.body.appendChild(tI);}tI.innerHTML='$time';</script>";

  2. 在CSS中创建并填充.ldtme{...}类。

答案 15 :(得分:0)

进一步扩展了Hamid的答案,我编写了一个可以重复启动和停止的帮助程序类(用于在循环内进行分析)。

   class ExecutionTime
   {
      private $startTime;
      private $endTime;
      private $compTime = 0;
      private $sysTime = 0;

      public function Start(){
         $this->startTime = getrusage();
      }

      public function End(){
         $this->endTime = getrusage();
         $this->compTime += $this->runTime($this->endTime, $this->startTime, "utime");
         $this->systemTime += $this->runTime($this->endTime, $this->startTime, "stime");
      }

      private function runTime($ru, $rus, $index) {
         return ($ru["ru_$index.tv_sec"]*1000 + intval($ru["ru_$index.tv_usec"]/1000))
         -  ($rus["ru_$index.tv_sec"]*1000 + intval($rus["ru_$index.tv_usec"]/1000));
      }

      public function __toString(){
         return "This process used " . $this->compTime . " ms for its computations\n" .
                "It spent " . $this->systemTime . " ms in system calls\n";
      }
   }

答案 16 :(得分:0)

要显示分钟和秒,您可以使用:

    $startTime = microtime(true);
    $endTime = microtime(true);
    $diff = round($endTime - $startTime);
    $minutes = floor($diff / 60); //only minutes
    $seconds = $diff % 60;//remaining seconds, using modulo operator
    echo "script execution time: minutes:$minutes, seconds:$seconds"; //value in seconds

答案 17 :(得分:0)

作为替代方案,您可以将此行放在代码块中并检查php日志,对于功能非常慢的功能它非常有用:

trigger_error("Task done at ". strftime('%H:%m:%S', time()), E_USER_NOTICE); 

要进行严肃的调试,请使用XDebug + Cachegrind,请参阅https://blog.nexcess.net/2011/01/29/diagnosing-slow-php-execution-with-xdebug-and-kcachegrind/

答案 18 :(得分:0)

只是为这次对话做出贡献:

  • 如果测量针对不同php文件中的两个点A和B会发生什么?

  • 如果我们需要不同的度量,例如基于时间、代码执行持续时间、外部资源访问持续时间,该怎么办?

  • 如果我们需要按类别组织我们的测量,每个类别都有不同的起点怎么办?

您怀疑我们需要一些全局变量才能被类对象或静态方法访问:我选择了第二种方法,这里是:

namespace g3;

class Utils {
   public function __construct() {}

   public static $UtilsDtStart = [];
   public static $UtilsDtStats = [];

   public static function dt() {
      global $UtilsDtStart, $UtilsDtStats;
      $obj = new \stdClass();
      $obj->start = function(int $ndx = 0) use (&$UtilsDtStart) {
         $UtilsDtStart[$ndx] = \microtime(true) * 1000;
      };
      $obj->codeStart = function(int $ndx = 0) use (&$UtilsDtStart) {
         $use = \getrusage();
         $UtilsDtStart[$ndx] = ($use["ru_utime.tv_sec"] * 1000) + ($use["ru_utime.tv_usec"] / 1000);
      };
      $obj->resourceStart = function(int $ndx = 0) use (&$UtilsDtStart) {
         $use = \getrusage();
         $UtilsDtStart[$ndx] = $use["ru_stime.tv_usec"] / 1000;
      };
      $obj->end = function(int $ndx = 0) use (&$UtilsDtStart, &$UtilsDtStats) {
         $t = @$UtilsDtStart[$ndx];
         if($t === null)
            return false;
         $end = \microtime(true) * 1000;
         $dt = $end - $t;
         $UtilsDtStats[$ndx][] = $dt;
         return $dt;
      };
      $obj->codeEnd = function(int $ndx = 0) use (&$UtilsDtStart, &$UtilsDtStats) {
         $t = @$UtilsDtStart[$ndx];
         if($t === null)
            return false;
         $use = \getrusage();
         $dt = ($use["ru_utime.tv_sec"] * 1000) + ($use["ru_utime.tv_usec"] / 1000) - $t;
         $UtilsDtStats[$ndx][] = $dt;
         return $dt;
      };
      $obj->resourceEnd = function(int $ndx = 0) use (&$UtilsDtStart, &$UtilsDtStats) {
         $t = @$UtilsDtStart[$ndx];
         if($t === null)
            return false;
         $use = \getrusage();
         $dt = ($use["ru_stime.tv_usec"] / 1000) - $t;
         $UtilsDtStats[$ndx][] = $dt;
         return $dt;
      };
      $obj->stats = function(int $ndx = 0) use (&$UtilsDtStats) {
         $s = @$UtilsDtStats[$ndx];
         if($s !== null)
            $s = \array_slice($s, 0);
         else
            $s = false;
         return $s;
      };
      $obj->statsLength = function() use (&$UtilsDtStats) {
         return \count($UtilsDtStats);
      };
      return $obj;
   }
}

现在您所要做的就是使用表示它是唯一组的索引调用属于特定类别的方法:

File A
------
\call_user_func_array(\g3\Utils::dt()->start, [0]);   // point A
...
File B
------
$dt = \call_user_func_array(\g3\Utils::dt()->end, [0]);  // point B

$dt 包含点 A 和 B 之间挂钟持续时间的毫秒数。

要估计运行 php 代码所需的时间:

File A
------
\call_user_func_array(\g3\Utils::dt()->codeStart, [1]);   // point A
...
File B
------
$dt = \call_user_func_array(\g3\Utils::dt()->codeEnd, [1]);  // point B

注意我们如何更改在方法中传递的索引。

代码基于当我们从函数返回对象/函数时发生的闭包效应(参见示例中重复的 \g3\Utils::dt())。

我使用 php 单元进行了测试,并在同一个测试文件中的不同测试方法之间进行了测试,到目前为止它表现良好!

希望对某人有所帮助!

答案 19 :(得分:-1)

返回microtime(true)-$ _SERVER [“ REQUEST_TIME_FLOAT”];