How to measure code performance in PHP?

30

To measure performance and compare two codes in javascript , I do the following 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 multiple times (1000, 10000, etc) inside a loop and note 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 the most 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 link

    
asked by anonymous 06.01.2014 / 22:08

6 answers

26

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

For example, if you have a certain algorithm running, you do not want to know just the total time, but how many times each function was called and how long each of them took.

This lets you know where the biggest "costs" are and optimize where it really matters. According to the Pareto Act , you could improve the performance of 80% of the application by setting 20% of the heaviest stretches.

Well, one of the ways to do this is installing xdebug in your PHP and performing a visual analysis with a tool like Webgrind . Here's an example of the result:

    
07.01.2014 / 12:55
12

The microtime function in my opinion is rather the most appropriate one, however 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 since it already returns the values in Bytes , I do not see why I do anything other than the lower initial value, now if you want something more complete or even better than that, I believe that just by using plugins, with your own PHP I believe this is the best way even ...

    
07.01.2014 / 00:03
6

By doing a lot of research, 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: link

    
07.01.2014 / 03:58
2

I made a small class for time measurement. It may 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";
    }

}

Usage:

$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 
    
30.07.2015 / 15:58
0

You can use XHProf ( link ). Originally it was developed by facebook, I find an excellent tool and has a very friendly interface.

    
14.03.2017 / 20:05
0

I found this forum when I was having the same problem of measuring my functions, requisitions, instanciamentos and everything. Seeking to find a simple function or class I 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 execution time , number of interactions and an assert true and false .

If you are familiar with the javascript tool QUnit, 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, since I chose to start the whole class by static methods, I avoided to create the concrete instance of the class so as not to interfere in the performance if there are many tests, but this is optional if I use the instantiation of the class in the standard Singleton . Here is a Usage template below.

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

Myrecommendationsforuseare:

  • Theclassissimple,whichjustifiesitsuseinsimplecases.
  • Therightwaytousetheclassistocreateaunittestfile,neverusetheclassinthesoftwarefiles.
  • Thefileofthisclassmustbethefirsttobeincludedinthetestfile,asitcontainstheheaderfunction,whichcancauseerrorsifyoudonotdoso
  • Thestaticmethod::on()isresponsibleforstartingthetest,whichmakesitmandatory.
  • The::on()methodfollowsthefollowingconvention:::on(string$nomeDoTeste,function$funcaoDeTeste,number$numeroDeIterações).Sothefirstargumentisastringthatspecifiesthetestname,thesecondargumentisananonymousfunctionthatvalidatesthetestandthethirdargumentisthenumberoftimesthatthesametestisrun.
  • TheanonymousfunctionreceivesanassertparameterwhichistheTesterclass,whichinturnprovidesthe::ok()
  • Thestatic::ok()methodisresponsibleforvalidatingthetestanddescribingtheaction,soitgetstwoarguments,thefirstshouldbeoftypebool,whichcertifiesthatthetestpassedandthesecondoftypestringthatdescribestheactiontestedasbelow.
    $assert::ok(true,"descrição do teste" );
    
28.12.2017 / 16:21