Seguimiento del tiempo de ejecución del script en PHP

289

PHP debe rastrear la cantidad de tiempo de CPU que un script en particular ha utilizado para imponer el límite max_execution_time.

¿Hay alguna manera de acceder a esto dentro del script? Me gustaría incluir algunos registros con mis pruebas sobre cuánta CPU se quemó en el PHP real (el tiempo no se incrementa cuando el script está en espera y esperando la base de datos).

Estoy usando una caja de Linux.

twk
fuente

Respuestas:

237

En sistemas unixoides (y también en php 7+ en Windows), puede usar getrusage , como:

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

Tenga en cuenta que no necesita calcular una diferencia si genera una instancia de php para cada prueba.

phihag
fuente
¿Se debe restar el valor al final del valor al comienzo del script? Estoy obteniendo algunos números realmente extraños si no lo hago. Como una página que tardó 0.05 segundos en generar, dice que tomó 6s de tiempo de CPU ... ¿es correcto? Ver aquí: blog.rompe.org/node/85
Darryl Hein
@Darryl Hein: Ah, y obtienes resultados extraños porque estás usando la concatenación de cadenas en lugar de la suma;)
phihag
@phihag También me da momentos extraños, que una página tardó 40 segundos en cálculos pero se cargó en 2 segundos. El número tiende a saltar entre 1.4 segundos y 40 segundos
Timo Huovinen
1
@TimoHuovinen ¿Qué valores obtiene exactamente para utime/ stime/ la hora del reloj de pared? ¿Y puede publicar un enlace a un ejemplo reproducible que muestre este comportamiento? ¿En qué versión de OS / php / servidor web estás? En cualquier caso, es posible que desee publicar una nueva pregunta y vincularla aquí.
phihag
44
Simplemente agregue una pequeña actualización: esta función ahora también es compatible con Windows.
ankush981
522

Si todo lo que necesita es el tiempo del reloj de pared, en lugar del tiempo de ejecución de la CPU, entonces es fácil de calcular:

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

Tenga en cuenta que esto incluirá el tiempo que PHP está sentado esperando recursos externos como discos o bases de datos, que no se usa para max_execution_time.

talal7860
fuente
38
Hola, esto rastrea el 'tiempo de reloj de pared', no el tiempo de CPU.
twk
18
Perfecto, estaba buscando una solución de seguimiento de tiempo de reloj de pared.
samiles
118

Versión más corta de la respuesta de 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);

Como se señaló, esto es 'tiempo de reloj de pared' no 'tiempo de CPU'

C. Lee
fuente
74

La forma más fácil:

<?php

$time1 = microtime(true);

//script code
//...

$time2 = microtime(true);
echo 'script execution time: ' . ($time2 - $time1); //value in seconds
joan16v
fuente
99
¿Cómo es esto diferente de la respuesta de talal7860 ...?
benomatis
@webeno Él no divide por 60... No hay diferencia de hecho.
A1rPun
[cómo 2] cómo esta respuesta no tiene ningún voto negativo? Es lo mismo que la respuesta anterior.
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
fuente
No obtuve el resultado en segundos
Deberías estar usando PHP 5.4.0
Joyal
29

Creé una clase ExecutionTime a partir de una respuesta phihag que puede usar de fábrica:

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

uso:

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

Nota: En PHP 5, la función getrusage solo funciona en sistemas Unix-oid. Desde PHP 7, también funciona en Windows.

Hamid Tavakoli
fuente
2
Nota: En Windows getrusagesolo funciona desde PHP 7.
Martin van Driel
@MartinvanDriel Anexé la nota. Gracias
Hamid Tavakoli
3
Supongo que si pones inicio en el constructor y terminas en la cadena, cada uso necesitaría 2 líneas de código menos. +1 para OOP
toddmo
13

Gringod en developerfusion.com da esta buena respuesta:

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

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

lencho patasplanas
fuente
11

Será más bonito si formatea la salida de segundos como:

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

imprimirá

Process took 6.45 seconds.

Esto es mucho mejor que

Process took 6.4518549156189 seconds.
Sinan Eldem
fuente
9

La forma más barata y sucia de hacerlo es simplemente hacer microtime()llamadas en los lugares de su código que desea comparar. Hágalo justo antes y después de las consultas de la base de datos y es simple eliminar esas duraciones del resto del tiempo de ejecución de su script.

Una sugerencia: su tiempo de ejecución de PHP rara vez será lo que haga que su script se agote. Si un script supera el tiempo de espera, casi siempre será una llamada a un recurso externo.

Documentación PHP microtime: http://us.php.net/microtime

danieltalsky
fuente
8

Creo que deberías mirar xdebug. Las opciones de creación de perfiles le ayudarán a conocer muchos elementos relacionados con el proceso.

http://www.xdebug.org/

Stewart Robinson
fuente
1
Solo asegúrese de no instalar xdebug en un servidor de producción con muchos sitios web. Produce una enorme cantidad de registros y puede abrumar una pequeña unidad SSD.
Corgalore
8

Para mostrar minutos y segundos puedes usar:

    $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
fuente
2

Escribí una función que verifica el tiempo de ejecución restante.

Advertencia: el recuento del tiempo de ejecución es diferente en Windows y en la plataforma 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);
}

Utilizando:

while (true) {
    // so something

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

Es posible que solo desee conocer el tiempo de ejecución de partes de su script. La forma más flexible de cronometrar partes o un script completo es crear 3 funciones simples (código de procedimiento que se proporciona aquí, pero puede convertirlo en una clase colocando el temporizador de clase {} a su alrededor y haciendo un par de ajustes). Este código funciona, solo copia y pega y ejecuta:

$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
fuente
1

$_SERVER['REQUEST_TIME']

mira eso también. es decir

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

Ampliando aún más la respuesta de Hamid, escribí una clase auxiliar que se puede iniciar y detener repetidamente (para perfilar dentro de un bucle).

   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
fuente
-1

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

ICP
fuente