Śledzenie czasu wykonania skryptu w PHP

289

PHP musi śledzić czas procesora wykorzystany przez dany skrypt w celu wymuszenia limitu max_execution_time.

Czy istnieje sposób na uzyskanie dostępu do tego w skrypcie? Chciałbym dołączyć do moich testów rejestrowanie, ile procesora zostało spalone w rzeczywistym PHP (czas nie jest zwiększany, gdy skrypt siedzi i czeka na bazę danych).

Używam Linux-a.

twk
źródło

Odpowiedzi:

238

W systemach unixoid (a także w php 7+ na Windowsie) możesz użyć getrusage , takiego jak:

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

Zauważ, że nie musisz obliczać różnicy, jeśli spawnujesz instancję php dla każdego testu.

phihag
źródło
Czy wartość na końcu należy odjąć od wartości na początku skryptu? Dostaję naprawdę dziwne liczby, jeśli nie. Jak strona, której wygenerowanie zajęło 0,05 sekundy, mówi, że zajęło to 6s czasu procesora ... czy to prawda? Zobacz tutaj: blog.rompe.org/node/85
Darryl Hein
@Darryl Hein: Aha, i dostajesz dziwne wyniki, ponieważ zamiast dodawania używasz konkatenacji łańcuchów;)
phihag
@ phihag Daje mi również dziwne czasy, że strona potrzebowała 40 sekund na obliczenia, ale ładowała się w 2 sekundy. Liczba skacze od 1,4 sekundy do 40 sekund
Timo Huovinen
1
@TimoHuovinen Jakie dokładnie wartości otrzymujesz za utime/ stime/ zegar ścienny? Czy możesz opublikować link do odtwarzalnego przykładu, który pokazuje to zachowanie? Na jakiej wersji OS / php / webserver jesteś? W każdym razie możesz opublikować nowe pytanie i link do niego tutaj.
phihag
4
Wystarczy dodać małą aktualizację: ta funkcja jest teraz obsługiwana również w systemie Windows.
ankush981
522

Jeśli potrzebujesz jedynie czasu naściennego zamiast czasu wykonania procesora, możesz łatwo obliczyć:

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

Zauważ, że będzie to obejmować czas oczekiwania PHP na zasoby zewnętrzne, takie jak dyski lub bazy danych, które nie są używane przez max_execution_time.

talal7860
źródło
38
Cześć - śledzi to „czas ścienny” - nie czas procesora.
twk
18
Idealnie, szukałem rozwiązania do śledzenia czasu naściennego.
samiles
118

Krótsza wersja odpowiedzi 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);

Jak wskazano, jest to „czas zegara ściennego”, a nie „czas procesora”

C. Lee
źródło
74

Najłatwiejszy sposób:

<?php

$time1 = microtime(true);

//script code
//...

$time2 = microtime(true);
echo 'script execution time: ' . ($time2 - $time1); //value in seconds
joan16v
źródło
9
czym to się różni od odpowiedzi talal7860 ...?
benomatis
@webeno On nie dzieli 60... Rzeczywiście nie ma różnicy.
A1rPun,
[jak 2] jak ta odpowiedź nie ma żadnych negatywnych opinii? to to samo co powyższa odpowiedź.
T.Todua
36
<?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";
?>
Joyal
źródło
Nie otrzymałem wyniku w kilka sekund
Powinieneś używać PHP 5.4.0
Joyal
29

Utworzyłem klasę ExecutionTime na podstawie odpowiedzi phihag, z której można korzystać po wyjęciu z pudełka:

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

stosowanie:

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

Uwaga: W PHP 5 funkcja getrusage działa tylko w systemach uniksowych. Od wersji PHP 7 działa również w systemie Windows.

Hamid Tavakoli
źródło
2
Uwaga: w systemie Windows getrusagedziała tylko od PHP 7.
Martin van Driel,
@MartinvanDriel Dołączyłem notatkę. Dzięki
Hamid Tavakoli,
3
Myślę, że jeśli zaczynasz od konstruktora, a kończysz tostring, każde użycie wymagałoby 2 linii kodu mniej. +1 dla OOP
toddmo
13

Gringod na developerfusion.com daje dobrą odpowiedź:

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

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

lencho patasplanas
źródło
11

Będzie ładniej, jeśli sformatujesz wynik sekund w następujący sposób:

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

wydrukuje

Process took 6.45 seconds.

To jest znacznie lepsze niż

Process took 6.4518549156189 seconds.
Sinan Eldem
źródło
9

Najtańszym i najbrudniejszym sposobem na to jest po prostu wykonywanie microtime()połączeń w miejscach w kodzie, które chcesz przetestować. Zrób to tuż przed zapytaniami do bazy danych i zaraz po niej, a usunięcie czasu trwania z pozostałej części czasu wykonywania skryptu jest proste.

Wskazówka: Twój czas wykonania PHP rzadko powoduje przekroczenie limitu czasu skryptu. Jeśli skrypt przekroczy limit czasu, prawie zawsze będzie to wezwanie do zewnętrznego zasobu.

Dokumentacja mikrotimowa PHP: http://us.php.net/microtime

danieltalsky
źródło
8

Myślę, że powinieneś spojrzeć na xdebug. Opcje profilowania pozwolą Ci szybko poznać wiele elementów związanych z procesem.

http://www.xdebug.org/

Stewart Robinson
źródło
1
Tylko upewnij się, że nie instalujesz xdebug na serwerze produkcyjnym z wieloma stronami internetowymi. Wytwarza ogromne ilości rejestrowania i może przytłoczyć mały dysk SSD.
Corgalore,
8

Aby wyświetlić minuty i sekundy, możesz użyć:

    $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
Aris
źródło
2

Napisałem funkcję, która sprawdza pozostały czas wykonania.

Ostrzeżenie: liczenie czasu wykonania jest inne w systemie Windows i na platformie 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);
}

Za pomocą:

while (true) {
    // so something

    if (!isRemainingMaxExecutionTimeBiggerThan(5000)) {
        // Time to die.
        // Safely close DB and done the iteration.
    }
}
Jaskółka oknówka
źródło
2

$_SERVER['REQUEST_TIME']

też to sprawdź. to znaczy

...
// your codes running
...
echo (time() - $_SERVER['REQUEST_TIME']);
T.Todua
źródło
co ciekawe $_SERVER['REQUEST_TIME']jest również dostępny w php-cli (gdzie nie ma serwera)
hanshenrik
1

Możesz chcieć znać tylko czas wykonywania części skryptu. Najbardziej elastycznym sposobem dzielenia czasu na części lub całego skryptu jest utworzenie 3 prostych funkcji (podany tutaj kod proceduralny, ale można go przekształcić w klasę, umieszczając wokół niego licznik czasu klasy {} i wprowadzając kilka poprawek). Ten kod działa, wystarczy skopiować, wkleić i uruchomić:

$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');
JG Estiot
źródło
0

Rozwijając dalej odpowiedź Hamida, napisałem klasę pomocnika, którą można wielokrotnie uruchamiać i zatrzymywać (do profilowania wewnątrz pętli).

   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";
      }
   }
Oded
źródło
-1

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

ICP
źródło