How to measure code performance in PHP?

Asked

Viewed 10,949 times

30

To measure performance and compare two codes in javascript, I do it like this, in the browser console:

// Vamos testar o código 1
console.time('teste1');
for (var i = 0; i++ < 10000;) {
    $('#table tr').addClass('destaque');
}
console.timeEnd('teste1');
// teste1: 389.000ms


// Vamos testar o código 2
console.time('teste2');
for (var i = 0; i++ < 10000;) {
    $('#table').find('tr').addClass('destaque');
}
console.timeEnd('teste2');
// teste2: 375.000ms

Quick explanation: just start a counter in milliseconds, run the code I want to test several times (1000, 10000, etc.) within a loop and note down the time. Then just do the same procedure for the other code to be tested and compare the times to see who has the best performance for the browser in question.

But what about PHP? Is the following method recommended?

$str = 'teste';
header('Content-Type: text/plain');

$time = microtime(1);
$mem = memory_get_usage();

for ($i = 0; $i++ < 100000;) {
    $str = strtr($str, 'e', 'x');
    // Comparar com: $str = str_replace('e', 'x', $str);
    // Comparar com: $str = preg_replace('/e/', 'x', $str);
}

echo 'Tempo: ', 1000 * (microtime(1) - $time), "ms\n";
echo 'Memória: ', (memory_get_usage() - $mem) / (1024 * 1024);

Based on http://php.dzone.com/articles/5-things-you-should-check-now

  • I suggest Xhprof! http://br1.php.net/xhprof Take a look here too: http://xhprof.io/

6 answers

27


As all the answers (including the question!) have said already, microtime is an option for simpler testing. However, deeper analysis requires the use of a Profiler.

For example, if you have a particular algorithm running, you don’t want to know just the total time, but how often each function was called and how long each of them took.

This allows us to know where the biggest "costs" are and to optimize where it really matters. According to Pareto law, you could improve the performance of 80% of the application by adjusting 20% of the heavier stretches.

Well, one of the ways to do that is installing the Xdebug in your PHP and performing a visual analysis with a tool like Webgrind. See an example of the result obtained:

inserir a descrição da imagem aqui

  • 1

    I didn’t know Webgrind, it will help me a lot this beginning of the year in a project that I need to optimize.

14

The function microtime in my opinion is yes the most appropriate, but you can use it in a slightly more refined way:

$inicio1 = microtime(true);
//Seu primeiro script
$total1 = microtime(true) - $inicio1;
echo 'Tempo de execução do primeiro script: ' . $total1;
$inicio2 = microtime(true);
//Seu segundo script
$total2 = microtime(true) - $inicio2;
echo 'Tempo de execução do segundo script: ' . $total2;

With regard to memory_get_usage() I also see it as the best option, and as it already returns the values in Bytes I see no reason to do anything other than the least initial end value, now if you want something more complete or even better than that, I believe that only using plugins, with your own PHP I believe this is the best way to do it...

7

Researching a lot, I realized that using microtime serves well. I created a function to compare the codes:

function comparar() {
    $funcoes = func_get_args();
    $vezes = 100000;

    for ($i = 0, $len = count($funcoes); $i < $len; $i++) {
        $time = microtime(1);
        $funcao = $funcoes[$i];

        for ($j = 0; $j++ < $vezes;) {
            $funcao();
        }

        $total = 1000 * (microtime(1) - $time);
        $media = $total / $vezes;

        echo 'Tempo total: ', round($total, 3), "ms\n";
        echo 'Tempo médio: ', round($media, 3), "ms\n\n";
    }
}

So to compare str_replace, strtr and preg_replace, just do:

header('Content-Type: text/plain');

comparar(
    function() {
        str_replace('e', 'x', 'teste');
    },

    function() {
        strtr('teste', 'e', 'x');
    },

    function() {
        preg_replace('/e/', 'x', 'teste');
    }
);

The end result is something like:

Tempo total: 347.955ms
Tempo médio: 0.003ms

Tempo total: 333.841ms
Tempo médio: 0.003ms

Tempo total: 455.327ms
Tempo médio: 0.005ms


See also:

Benchmarks made with microtime: http://www.phpbench.com/

  • Basically, this is not the same code inserted into the question only inside a Function?

  • That’s it. I realized that this solution works well and many people use it this way.

2

I made a small class for time measurement. Maybe it will be useful to someone:

class TemporizacaoHelper {

    private $inicio;

    public function __construct() {
        $this->inicio = microtime(true);
    }

    public function iniciar() {
        $this->inicio = microtime(true);
    }

    public function segundos() {
        return microtime(true) - $this->inicio;
    }

    public function tempo() {
        $segs = $this->segundos();
        $dias = floor($segs / 86400);
        $segs -= $dias * 86400;
        $horas = floor($segs / 3600);
        $segs -= $horas * 3600;
        $minutos = floor($segs / 60);
        $segs -= $minutos * 60;
        $microsegs = ($segs - floor($segs)) * 1000;
        $segs = floor($segs);

        return 
            (empty($dias) ? "" : $dias . "d ") . 
            (empty($horas) ? "" : $horas . "h ") . 
            (empty($minutos) ? "" : $minutos . "m ") . 
            $segs . "s " .
            $microsegs . "ms";
    }

}

Use:

$th = new TemporizacaoHelper();
<..código sendo medido..>
$echo $th->tempo();
$th->iniciar(); // se for o caso
<..código sendo medido..>
$echo $th->tempo();

// resultado: 4d 17h 34m 57s 0.00095367431640625ms 

0

-1

I found this forum when I was having the same problem measuring my functions, requisitions, instantiations and everything. Seeking to find a simple function or class saw in the answers above good ideas, and from them, I could make some improvements.

So I created a class that follows the idea of jquery Quinit, is very simple, but meets the minimum of measure runtime, the number of interactions and a assert true and false.

If you are familiar with the Qunit javascript tool, you will have no difficulty using this php class.

<?php
#Tester.php 

header ( 'Content-Type: text/html; charset=utf-8' );
error_reporting ( E_ALL ^ E_NOTICE );
error_reporting ( E_ALL );
ini_set ( 'error_reporting', E_ALL );
ini_set ( 'display_startup_errors', TRUE );
ini_set ( "display_errors", TRUE );
ini_set ( "default_charset", TRUE );

class Tester 
{
    private static $offset = 0.000007;
    private static $success = array ( "#0A0", "rgba(0,190,0,0.1)" );
    private static $error = array ( "#C00", "rgba(190,0,0,0.1)" );

    private static $status = false;
    private static $name = null;
    private static $msg = null;
    private static $repeat = 1;

    private static $timeOfTest = 0;
    private static $timeOfEachTest = 0;

    private static function reset ( ) 
    {
        self::$status = false;
        self::$name = null;
        self::$msg = null;
        self::$repeat = 1;
        self::$timeOfTest = 0;
        self::$timeOfEachTest = 0;

    }

    private static function assert ( ) 
    { 
        return "Tester";
    }

    public static function ok ( bool $status = false, string $msg = null ): bool
    {
        self::$msg = $msg;
        return self::$status = $status;
    }

    private static function inner ( ) 
    {   
        $set = ( self::$status !== false ) ? self::$success : self::$error;

        $title = '<span><b>'.self::$name.' </b><small>'.round ( self::$timeOfEachTest, 6 ).'ms <sub>( x'.self::$repeat.' )</sub></small> </span>';
        $time = '<span style="float: right;text-align: right; rigth: 0;"><b>Tempo total: '.round ( ( self::$timeOfTest / 1000 ), 2 ).'s <small> ( '. round ( self::$timeOfTest, 4 ).'ms )</b></small></span>';
        $msg = '<div style="font-size: 10pt;">'.self::$msg.'</div>';

        echo '<div style="font-family: verdana, sans-serif;font-size: 10pt; display: block; border: solid 1px '.$set [ 0 ].'; background-color: '.$set [ 1 ].'; padding: 5px; margin: 2px 0;"><div style="display: block; margin-bottom: 4px; border-bottom: solid 1px '.$set [ 1 ].'; padding-bottom: 3px; width:100%;">'.$title.$time.'</div>'.$msg.'</div>';
    }  

    private static function sum ( array $array = null ): float
    {
        return array_reduce ( $array, function ( $previous, $item ) {
            return $previous += $item;
        } );
    }

    public static function on ( string $name = null, Closure $fn = null, int $repeat = 1 ) 
    {
        self::reset ( );

        if ( is_string ( $name ) && $fn instanceof Closure && is_numeric ( self::$repeat ) ) {

            self::$name = $name;
            self::$repeat = $repeat;

            $timeOfFunctions = array ( );
            $init = microtime ( 1 );

            for ( $i = 0; $i < self::$repeat; $i++ ) {
                $time = microtime ( 1 );
                $fn ( self::assert ( ) );
                array_push ( $timeOfFunctions, ( ( microtime ( 1 ) - $time ) * 1000 ) );
            };

            $timeOfExec = ( ( ( microtime ( 1 ) - $init ) - self::$offset ) * 1000 );
            $timeOfEachExec = ( $timeOfExec / self::$repeat );

            $totalOfFunctions = self::sum ( $timeOfFunctions );
            $timeOfEachFunction = ( $totalOfFunctions / count ( $timeOfFunctions ) );

            self::$timeOfTest = ( ( $timeOfExec + $totalOfFunctions ) / 2 );
            self::$timeOfEachTest = ( ( $timeOfEachExec + $timeOfEachFunction ) / 2 );

        } else {
            self::$name = ( empty ( self::$name ) ) ? "Error!" : self::$name;
            self::$msg = " Erro de Sintaxe. Favor verificar os argumentos de entrada do teste.";
        };

        self::inner ( );
    } 
}

#Tester::on ( "test 1", function ( $assert ) { $assert::ok ( true, "msg" ); }, 1000 );

The Use is simple, because I chose to start the whole class with static methods, I avoided creating the concrete instance of the class in order not to interfere in the performance, in case there are many tests, but this is optional if using the instantiation of the class in the Singleton standard. Follow a Usage Template below.

Tester::on ( "nome do teste", function ( $assert ) {
    $assert::ok ( true, "descrição do teste" );
}, 1 );

Exemplo do tester rodando

My recommendations for use are:

  • The class is simple, which justifies its use in simple cases.
  • The right way to use the class is to create a unit test file, never use the class in the software files.
  • The file of this class must be the first to be included in the test file as it contains the header function, which can cause errors if it does not do so
  • The static method ::on ( ) is responsible for initiating the test, which makes it mandatory.
  • The method ::on ( ) follows the following convention:
    ::on ( string $nomeDoTeste, function $funcaoDeTeste, number $numeroDeIterações ). So the first argument is a string which specifies the test name, the second argument is a anonymous function which validates the test and the third argument is number the same test is run.
  • The anonymous function receives an "assert" parameter which is the class Tester, this in turn makes available the static aid ::okay ( ).
  • The static method ::okay ( ) is responsible for validating the test and describing the action, so it gets two arguments, the first must be of the bool type, which certifies that the test has passed and the second of the string type which describes the action tested as below.

    $assert::ok ( true, "descrição do teste" );
    

Browser other questions tagged

You are not signed in. Login or sign up in order to post.