为了强制执行max_execution_time限制,PHP必须跟踪特定脚本所使用的CPU时间。

是否有一种方法可以在脚本中访问它?我希望在测试中包含一些关于实际PHP中消耗了多少CPU的日志记录(当脚本等待数据库时,时间不会增加)。

我用的是Linux机顶盒。


最便宜也是最麻烦的方法是在代码中想要进行基准测试的地方进行microtime()调用。在数据库查询之前和之后执行它,从脚本执行时间的其余部分中删除这些持续时间就很简单了。

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

PHP微时间文档: http://us.php.net/microtime


在unix系统上(在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实例,则不需要计算差异。


我认为您应该看看xdebug。分析选项将为您了解许多与流程相关的项目提供一个良好的开端。

http://www.xdebug.org/


如果你需要的是挂钟时间,而不是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没有使用这个时间。


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/)


<?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";
?>

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);

正如所指出的,这是“wallclock时间”而不是“cpu时间”


我创建了一个ExecutionTime类的phihag答案,你可以使用开箱即用:

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系统。从PHP 7开始,它也可以在Windows上工作。


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

$tstart = 0;
$tend = 0;

function timer_starts()
{
global $tstart;

$tstart=microtime(true); ;

}

function timer_ends()
{
global $tend;

$tend=microtime(true); ;

}

function timer_calc()
{
global $tstart,$tend;

return (round($tend - $tstart,2));
}

timer_starts();
file_get_contents('http://google.com');
timer_ends();
print('It took '.timer_calc().' seconds to retrieve the google page');

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

警告:执行时间计数在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.
    }
}

作为一种替代方法,你可以把这一行放在你的代码块中,并检查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/


如果你像这样格式化秒的输出,它会更漂亮:

echo "Process took ". number_format(microtime(true) - $start, 2). " seconds.";

将打印

Process took 6.45 seconds.

这比

Process took 6.4518549156189 seconds.

进一步扩展Hamid的回答,我写了一个可以重复启动和停止的helper类(用于在循环中进行分析)。

   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";
      }
   }

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

    $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

return microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];


$_SERVER[REQUEST_TIME“]

看看这个。即。

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

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

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

现在,在上述函数的帮助下,我们可以像这样跟踪时间

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

每次调用startTime函数都会启动一个单独的时间跟踪器。所以你可以想启动多少就启动多少,也可以在任何你想要的地方停止它们。


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

为了不扭曲结果,并与页面中的内容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>";

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

替换回显…;与以下部分分开: echo "<script>if(!tI){var tI=document.createElement('div');tI. classname ='ldtme';document.body.appendChild(tI);}tI. innerhtml ='$time'; 在CSS中创建并填充.ldtme{…}类。


我想说的是:

如果测量目标是不同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单元进行了测试,在同一测试文件中的不同测试方法之间,到目前为止它表现良好!

希望这能帮助到别人!


这里列出了几种方法。但每个人都有自己的优点和缺点。而且(在我看来)所有较长的答案的可读性都很糟糕。

所以我决定把这些都放在一个答案中,这是容易使用和阅读的。

使用

$start = get_timers();
for( $i = 0; $i < 100000; $i++ ){
  // Code to check
}
$end = get_timers();
display_timer_statistics( $start, $end );

函数定义

function display_timer_statistics( $start_timers, $end_timers ){

    // Settings
    $key_width = '100px';
    $decimals = 4;
    $decimals_wallclock = $decimals;
    $decimals_request_time_float = $decimals;

    // Variables
    $start_resource_usage_timer = $start_timers[0];
    $start_wallclock = $start_timers[1];
    $end_resource_usage_timer = $end_timers[0];
    $end_wallclock = $end_timers[1];

    // # User time
    // Add seconds and microseconds for the start/end, and subtract from another
    $end_user_time_seconds = $end_resource_usage_timer["ru_utime.tv_sec"]*1000;
    $end_user_time_microseconds = intval($end_resource_usage_timer["ru_utime.tv_usec"]/1000);
    $start_user_time_seconds = $start_resource_usage_timer["ru_utime.tv_sec"]*1000;
    $start_user_time_microseconds = intval($start_resource_usage_timer["ru_utime.tv_usec"]/1000);
    $total_user_time = ($end_user_time_seconds + $end_user_time_microseconds) - ($start_user_time_seconds + $start_user_time_microseconds);

    // # System time
    // Add seconds and microseconds for the start/end, and subtract from another
    $end_system_time_seconds = $end_resource_usage_timer["ru_stime.tv_sec"]*1000;
    $end_system_time_microseconds = intval($end_resource_usage_timer["ru_stime.tv_usec"]/1000);
    $start_system_time_seconds = $start_resource_usage_timer["ru_stime.tv_sec"]*1000;
    $start_system_time_microseconds = intval($start_resource_usage_timer["ru_stime.tv_usec"]/1000);
    $total_system_time = ($end_system_time_seconds + $end_system_time_microseconds) - ($start_system_time_seconds + $start_system_time_microseconds);

    // Wallclock
    $total_wallclock_time = number_format( ( $end_wallclock - $start_wallclock), $decimals_wallclock );

    // Server request_time_float
    $request_time_float = microtime(true) - $_SERVER["REQUEST_TIME_FLOAT"];
    $request_time_float = number_format( $request_time_float, $decimals_request_time_float );

    // Print
    $span_start = "<span style='width: $key_width; display: inline-block;'>";
    $span_end = "</span>";

    $output = "# RUNTIME AND TIMERS " . PHP_EOL ;
    $output .= PHP_EOL;
    $output .= $span_start . $total_user_time . $span_end . " User time (utime)" . PHP_EOL;
    $output .= $span_start . $total_system_time . $span_end . " System time (stime)" . PHP_EOL;
    $output .= PHP_EOL;
    $output .= $span_start . $total_wallclock_time . $span_end . " Wallclock" . PHP_EOL;
    $output .= PHP_EOL;
    $output .= $span_start . $request_time_float . $span_end . " REQUEST_TIME_FLOAT" . PHP_EOL . PHP_EOL . PHP_EOL;

    echo nl2br( $output );
}

function get_timers(){
    return [ getrusage(), microtime( true ) ];
}

术语表

这些都是从PHP文档中获取的

挂钟=花了多长时间 ru =资源使用率 utime =用户使用的时间 stime =使用的系统时间 tv_sec =秒。 tv_usec =以微秒计。 电视= ??不知道