Maneira mais simples de perfilar um script PHP

Qual é a maneira mais fácil de perfilar um script PHP?

Eu adoraria abordar algo que me mostra um despejo de todas as chamadas de function e quanto tempo eles levaram, mas também estou bem com colocar algo em torno de funções específicas.

Experimentei com a function microtime :

$then = microtime(); myFunc(); $now = microtime(); echo sprintf("Elapsed: %f", $now-$then); 

mas que às vezes me dá resultados negativos. Além disso, tem muitos problemas para polvilhar isso em todo o meu código.

    A extensão PECL APD é usada da seguinte forma:

     < ?php apd_set_pprof_trace(); //rest of the script ?> 

    Depois, analise o arquivo gerado usando pprofp .

    Exemplo de saída:

     Trace for /home/dan/testapd.php Total Elapsed Time = 0.00 Total System Time = 0.00 Total User Time = 0.00 Real User System secs/ cumm %Time (excl/cumm) (excl/cumm) (excl/cumm) Calls call s/call Memory Usage Name -------------------------------------------------------------------------------------- 100.0 0.00 0.00 0.00 0.00 0.00 0.00 1 0.0000 0.0009 0 main 56.9 0.00 0.00 0.00 0.00 0.00 0.00 1 0.0005 0.0005 0 apd_set_pprof_trace 28.0 0.00 0.00 0.00 0.00 0.00 0.00 10 0.0000 0.0000 0 preg_replace 14.3 0.00 0.00 0.00 0.00 0.00 0.00 10 0.0000 0.0000 0 str_replace 

    Você quer xdebug, eu acho. Instale-o no servidor, ligue-o, bombeie a saída através do kcachegrind (para o linux) ou do wincachegrind (para o Windows) e mostrará alguns charts bonitos que detalham os horários exatos, as contagens e o uso da memory (mas você precisa de outra extensão para isso).

    Rochas, seriamente: D

    Não são necessárias extensões, basta usar essas duas funções para perfis simples.

     // Call this at each point of interest, passing a descriptive string function prof_flag($str) { global $prof_timing, $prof_names; $prof_timing[] = microtime(true); $prof_names[] = $str; } // Call this when you're done and want to see the results function prof_print() { global $prof_timing, $prof_names; $size = count($prof_timing); for($i=0;$i< $size - 1; $i++) { echo "{$prof_names[$i]}
    "; echo sprintf("   %f
    ", $prof_timing[$i+1]-$prof_timing[$i]); } echo "{$prof_names[$size-1]}
    "; }

    Aqui está um exemplo, chamando prof_flag () com uma descrição em cada ponto de controle e prof_print () no final:

     prof_flag("Start"); include '../lib/database.php'; include '../lib/helper_func.php'; prof_flag("Connect to DB"); connect_to_db(); prof_flag("Perform query"); // Get all the data $select_query = "SELECT * FROM data_table"; $result = mysql_query($select_query); prof_flag("Retrieve data"); $rows = array(); $found_data=false; while($r = mysql_fetch_assoc($result)) { $found_data=true; $rows[] = $r; } prof_flag("Close DB"); mysql_close(); //close database connection prof_flag("Done"); prof_print(); 

    A saída parece assim:

    Começar
    0.004303
    Conecte-se ao DB
    0.003518
    Realizar consulta
    0.000308
    Recuperar dados
    0.000009
    Fechar DB
    0.000049
    Feito

    Se subtrair microtimes lhe dá resultados negativos, tente usar a function com o argumento true ( microtime(true) ). Com true , a function retorna um flutuador em vez de uma string (como acontece se for chamado sem argumentos).

    Honestamente, eu vou argumentar que o uso do NewRelic para perfis é o melhor.

    É uma extensão PHP que não parece diminuir o tempo de execução e eles fazem o monitoramento para você, permitindo uma análise decente. Na versão cara eles permitem pesados ​​drill down (mas não podemos pagar o seu modelo de preços).

    Ainda assim, mesmo com o plano gratuito / padrão, é óbvio e simples, onde é a maior parte da fruta de baixa suspensão. Eu também gosto que também pode dar uma idéia sobre as interações DB.

    captura de tela de uma das interfaces ao perfilar

    PECL XHPROF parece interensting também. Possui interface HTML clicável para visualizar relatórios e documentação bastante direta. Ainda tenho que testá-lo.

    Cruzando a minha referência da SO Documentation beta que está ficando offline.

    Perfis com XDebug

    Uma extensão para PHP chamada Xdebug está disponível para auxiliar no perfil de aplicativos PHP , bem como em debugging em tempo de execução. Ao executar o perfil, a saída é gravada em um arquivo no formato binário chamado “cachegrind”. Os aplicativos estão disponíveis em cada plataforma para analisar esses arquivos. Não são necessárias alterações no código do aplicativo para executar esse perfil.

    Para habilitar o perfil, instale a extensão e ajuste as configurações do php.ini. Algumas distribuições Linux vêm com pacotes padrão (por exemplo, o pacote php-xdebug do Ubuntu). No nosso exemplo, vamos executar o perfil opcionalmente com base em um parâmetro de solicitação. Isso nos permite manter as configurações estáticas e ativar o perfil somente conforme necessário.

     // Set to 1 to turn it on for every request xdebug.profiler_enable = 0 // Let's use a GET/POST parameter to turn on the profiler xdebug.profiler_enable_trigger = 1 // The GET/POST value we will pass; empty for any value xdebug.profiler_enable_trigger_value = "" // Output cachegrind files to /tmp so our system cleans them up later xdebug.profiler_output_dir = "/tmp" xdebug.profiler_output_name = "cachegrind.out.%p" 

    Em seguida, use um cliente da Web para fazer uma solicitação no URL do seu aplicativo que deseja perfis, por exemplo,

     http://example.com/article/1?XDEBUG_PROFILE=1 

    À medida que a página processa, ele irá escrever em um arquivo com um nome semelhante ao

     /tmp/cachegrind.out.12345 

    Por padrão, o número no nome do arquivo é o ID do processo que o escreveu. Isso é configurável com a configuração xdebug.profiler_output_name .

    Observe que ele irá escrever um arquivo para cada solicitação / processo PHP executado. Assim, por exemplo, se você deseja analisar uma publicação de formulário, um perfil será escrito para o pedido GET para exibir o formulário HTML. O parâmetro XDEBUG_PROFILE precisará ser passado para a solicitação POST subsequente para analisar a segunda solicitação que processa o formulário. Portanto, ao perfilar, às vezes é mais fácil executar curl para POST um formulário diretamente.

    Uma vez escrito, o cache do perfil pode ser lido por um aplicativo como o KCachegrind ou o Webgrind .

    KCachegrind

    Isso exibirá informações, incluindo:

    • Funções executadas
    • Tempo de chamada, tanto em si como incluindo chamadas de function subsequentes
    • Número de vezes que cada function é chamada
    • Gráficos de chamadas
    • Links para código fonte

    Obviamente, o ajuste de desempenho é muito específico para os casos de uso de cada aplicativo. Em geral, é bom procurar:

    • Chamadas repetidas para a mesma function que você não esperaria ver. Para as funções que processam e consultam dados, estas podem ser as principais oportunidades para o seu aplicativo armazenar em cache.
    • Funções de funcionamento lento. Onde a aplicação está passando a maior parte do tempo? A melhor remuneração no ajuste de desempenho está focada nas partes da aplicação que consomem a maior parte do tempo.

    Nota : O Xdebug, e em particular os resources de criação de perfil, são muito intensivos em resources e retardam a execução do PHP. É recomendável não executá-los em um ambiente de servidor de produção.

    Eu gosto de usar phpDebug para criação de perfil. http://phpdebug.sourceforge.net/www/index.html

    Ele produz todo o tempo / uso de memory para qualquer SQL usado, bem como todos os arquivos incluídos. Obviamente, funciona melhor no código que é abstraído.

    Para o perfil de function e de class, vou usar o microtime() + get_memory_usage() + get_peak_memory_usage() .

    Provavelmente, BlackFire tentaria.

    Há esta virtualBox que coloquei usando puphpet , para testar diferentes frameworks de php que coms com o BlackFire, sinta-se livre de garfo e / ou distribua, se necessário 🙂

    https://github.com/webit4me/PHPFrameworks

    Perfil do pobre perfil, não são necessárias extensões. Suporta perfis nesteds e porcentagem do total:

     function p_open($flag) { global $p_times; if (null === $p_times) $p_times = []; if (! array_key_exists($flag, $p_times)) $p_times[$flag] = [ 'total' => 0, 'open' => 0 ]; $p_times[$flag]['open'] = microtime(true); } function p_close($flag) { global $p_times; if (isset($p_times[$flag]['open'])) { $p_times[$flag]['total'] += (microtime(true) - $p_times[$flag]['open']); unset($p_times[$flag]['open']); } } function p_dump() { global $p_times; $dump = []; $sum = 0; foreach ($p_times as $flag => $info) { $dump[$flag]['elapsed'] = $info['total']; $sum += $info['total']; } foreach ($dump as $flag => $info) { $dump[$flag]['percent'] = $dump[$flag]['elapsed']/$sum; } return $dump; } 

    Exemplo:

     < ?php p_open('foo'); sleep(1); p_open('bar'); sleep(2); p_open('baz'); sleep(3); p_close('baz'); sleep(2); p_close('bar'); sleep(1); p_close('foo'); var_dump(p_dump()); 

    Rendimentos:

     array:3 [ "foo" => array:2 [ "elapsed" => 9.000766992569 "percent" => 0.4736904954747 ] "bar" => array:2 [ "elapsed" => 7.0004580020905 "percent" => 0.36841864946596 ] "baz" => array:2 [ "elapsed" => 3.0001420974731 "percent" => 0.15789085505934 ] ] 

    Para benchmarking, como em seu exemplo, uso o pacote Pear Benchmark . Você define marcadores para medir. A class também fornece alguns helpers de apresentação, ou você pode processar os dados conforme entender.

    Eu realmente o envolvi em outra class com um método __destruct. Quando um script é encerrado, a saída é registrada via log4php para o syslog, então eu tenho muitos dados de desempenho para trabalhar.

    O XDebug não é estável e nem sempre está disponível para uma versão particular do php. Por exemplo, em alguns servidores, eu ainda executo o php-5.1.6, – é o que vem com o RedHat RHEL5 (e o Btw ainda recebe atualizações para todos os problemas importantes) e o XDebug recente nem sequer compila com este php. Então eu terminei com a mudança para o depurador DBG. O seu benchmarking php fornece tempo para funções, methods, módulos e linhas pares.