O que há de errado com artigos populares dizendo que foo é mais rápido que o bar?

Nota do tradutor: Eu também pensei que o tempo para os artigos é "O que é mais rápido - aspas simples ou duplas?" Demorou mais 10 anos atrás. Mas aqui um artigo semelhante ("Quais truques de desempenho realmente funcionam") recentemente obteve uma classificação relativamente alta no Reddit e chegou ao resumo do PHP no Habré. Por isso, decidi traduzir o artigo com uma análise crítica desses e "testes" semelhantes.


Existem muitos artigos (e até sites inteiros) dedicados ao lançamento de vários testes comparando o desempenho de várias construções sintáticas e afirmando com base nisso que um é mais rápido que o outro.


Problema principal


Tais testes estão incorretos por vários motivos, desde a pergunta a erros de implementação. Mas o mais importante - esses testes são sem sentido e, ao mesmo tempo, prejudiciais.


  • Eles não têm sentido porque não têm valor prático. Nenhum projeto real foi acelerado usando os métodos fornecidos nesses artigos. Só porque não as diferenças de sintaxe são importantes para o desempenho, mas para o processamento de dados.
  • Eles são prejudiciais porque levam ao surgimento das superstições mais loucas e - ainda pior - incentivam leitores desavisados ​​a escrever códigos ruins, pensando que eles "os otimizam".

Isso deve ser suficiente para fechar a questão. Mas mesmo que você aceite as regras do jogo e finja que esses "testes" têm pelo menos algum sentido, acontece que os resultados deles são reduzidos apenas para demonstrar a falta de conhecimento do testador e sua falta de experiência.


Single versus Double


Tome as citações notórias, "único versus duplo". Obviamente, nenhuma cotação é mais rápida. Em primeiro lugar, existe o cache de opcode , que armazena o resultado da análise do script PHP no cache. Nesse caso, o código PHP é salvo no formato opcode, onde os mesmos literais de string são armazenados como entidades absolutamente idênticas, independentemente de quais aspas foram usadas no script PHP. O que significa a ausência de uma diferença teórica no desempenho.


Porém, mesmo se não usarmos o cache do opcode (embora devamos, se nossa tarefa for realmente aumentar o desempenho), descobriremos que a diferença no código de análise é tão pequena (várias transições condicionais comparando caracteres de byte único, literalmente várias instruções do processador) que serão absolutamente indetectável. Isso significa que qualquer resultado obtido demonstrará apenas problemas no ambiente de teste. Há um artigo muito detalhado, Desaprovando o mito do desempenho de aspas simples do desenvolvedor principal do PHP Nikita Popov, que analisa esse problema em detalhes. No entanto, um testador energético aparece quase todo mês para revelar à sociedade uma “diferença” imaginária no desempenho.


Inconsistências lógicas


Alguns testes geralmente não têm sentido, simplesmente do ponto de vista de se fazer a pergunta: por exemplo, o teste intitulado "O lançamento é realmente uma operação muito cara?" esta é essencialmente a pergunta "É realmente que processar um erro será mais caro do que não processar?". Ta falando serio Obviamente, adicionar algumas funcionalidades básicas ao código o tornará "mais lento". Mas isso não significa que nenhuma nova funcionalidade precise ser adicionada, sob um pretexto tão ridículo. Se você fala assim, o programa mais rápido é aquele que não faz nada! O programa deve ser útil e funcionar sem erros em primeiro lugar. E somente depois que isso for alcançado, e somente se funcionar lentamente, precisará ser otimizado. Mas se a pergunta em si não faz sentido, por que se preocupar em testar o desempenho? É engraçado que o testador não tenha conseguido implementar corretamente mesmo esse teste sem sentido, que será mostrado na próxima seção.


Ou outro exemplo, um teste intitulado " $row[id] será realmente mais lento que $row['id'] ?" esta é essencialmente a pergunta "Qual código é mais rápido - aquele que funciona com erros ou sem?" (como escrever um id sem aspas nesse caso é um erro do nível E_NOTICE , e esse tipo de gravação será descontinuado em versões futuras do PHP). WTF? Qual é o sentido de medir geralmente o desempenho do código de erro? O erro deve ser corrigido simplesmente porque é um erro, e não porque tornará o código mais lento. É engraçado que o testador não tenha conseguido implementar corretamente mesmo esse teste sem sentido, que será mostrado na próxima seção.


Qualidade de teste


E, novamente - mesmo um teste sem conhecimento de causa deve ser consistente, consistente - ou seja, medir valores comparáveis. Mas, como regra, esses testes são realizados com o calcanhar esquerdo e, como resultado, os resultados obtidos são insignificantes e não são relevantes para a tarefa.


Por exemplo, nosso testador estúpido se comprometeu a medir "o uso excessivo do operador try..catch ". Mas no teste atual, ele mediu não apenas o try catch , mas também o throw , lançando uma exceção a cada iteração do loop. Mas esse teste é simplesmente incorreto, porque na vida real os erros não ocorrem a cada execução de script.


Obviamente, os testes não devem ser realizados nas versões beta do PHP e não devem comparar as soluções convencionais com as experimentais. E se o testador se comprometer a comparar a "velocidade de análise de json e xml", ele não deverá usar a função experimental nos testes.


Alguns testes simplesmente demonstram um completo mal-entendido pelo testador da tarefa definida por ele. Um exemplo semelhante de um artigo publicado recentemente já foi mencionado acima: o autor do teste tentou descobrir se o código que causou o erro ("Uso de constante indefinida") seria mais lento que o código sem erros (que usa uma literal de cadeia sintaticamente correta), mas falhou mesmo com esse teste obviamente sem sentido, comparando o desempenho de um número citado com o desempenho de um número escrito sem aspas. Obviamente, você pode escrever números sem aspas no PHP (ao contrário de strings) e, como resultado, o autor testou uma funcionalidade completamente diferente, recebendo resultados incorretos.


Há outros problemas a serem considerados, como o ambiente de teste. Existem extensões para o PHP, como o XDebug, que podem ter um grande impacto nos resultados dos testes. Ou o cache do código de operação já mencionado, que deve ser incluído durante os testes de desempenho, para que os resultados do teste possam fazer pelo menos algum sentido.


Como o teste é feito também é importante. Como o processo PHP morre completamente após cada solicitação, faz sentido testar o desempenho de todo o ciclo de vida, começando com a criação de uma conexão com um servidor da Web e terminando com o fechamento dessa conexão. Existem utilitários como o Apache benchmark ou o Siege que permitem fazer isso.


Melhoria real do desempenho


Tudo isso é bom, mas que conclusão o leitor deve tirar deste artigo? Quais testes de desempenho são inúteis por definição? Claro que não. Mas o que realmente importa é a razão pela qual eles devem começar. Testar do zero é uma perda de tempo. Sempre deve haver um motivo específico para executar testes de desempenho. E esse motivo é chamado de "criação de perfil" . Quando seu aplicativo começa a ser executado lentamente, é necessário criar perfis, o que significa medir a velocidade de várias seções do código para encontrar a mais lenta. Depois que um site é encontrado, precisamos determinar a causa. Na maioria das vezes, isso é muito maior que o necessário, a quantidade de dados processados ​​ou uma solicitação para uma fonte de dados externa. No primeiro caso, a otimização consistirá em reduzir a quantidade de dados processados ​​e, no segundo caso, em cache os resultados da consulta.


Por exemplo, em termos de desempenho, não faz diferença se usamos um loop explicitamente prescrito ou a função PHP incorporada para processar matrizes (que são essencialmente apenas açúcar sintático). O que é realmente importante é a quantidade de dados que transmitimos para processamento. Se for excessivamente grande, devemos apará-lo ou mover o processamento para outro lugar (para o banco de dados). Isso nos dará um enorme aumento de desempenho que será real . Embora seja improvável que a diferença entre os métodos de chamar o loop para processamento de dados seja visível.


Somente depois de executar essas melhorias obrigatórias de desempenho, ou se não podemos reduzir a quantidade de dados processados, podemos iniciar os testes de desempenho. Mas, novamente, esses testes não devem ser feitos do zero. Para começar a comparar o desempenho de um loop explícito e de uma função embutida, precisamos ter certeza de que o loop é a causa do problema, não seu conteúdo (spoiler: é claro, esse é o conteúdo).


Um exemplo recente da minha prática: no código, havia uma consulta usando o Doctrine Query Builder, que deveria receber vários milhares de parâmetros. A consulta em si é rápida o suficiente, mas o Doctrine leva um bom tempo para digerir vários milhares de parâmetros. Como resultado, a consulta foi reescrita em SQL puro e os parâmetros foram transferidos para o método execute () da biblioteca PDO, que lida com muitos parâmetros quase instantaneamente.


Isso significa que eu nunca usarei o Doctrine Query Builder? Claro que não. É perfeito para 99% das tarefas e continuarei a usá-lo para todas as consultas. E somente em casos excepcionais vale a pena usar um método menos conveniente, mas mais produtivo.


A consulta e os parâmetros para esta seleção foram construídos em um loop. Se eu tivesse uma idéia estúpida para lidar com a forma como o ciclo é chamado, simplesmente perderia tempo sem nenhum resultado positivo. E esta é a essência de todas as otimizações de desempenho: otimizar apenas o código que é executado lentamente no seu caso específico. E não o código considerado lento há muito tempo, em uma galáxia distante e distante, ou o código que ocorreu a alguém chamar lento com base em testes sem sentido.

Source: https://habr.com/ru/post/pt419743/


All Articles