O código de operação PHP gerado pelo motor PHP é fortemente influenciado pela forma como você escreve seu código, não apenas em termos de número de declarações para concluir uma tarefa. Claramente, isso importa muito e acho que é óbvio para você.
O que pode ser menos óbvio é que até a sintaxe do código pode mudar completamente o código de operação gerado, causando muito overheard para o CPU da máquina para executar o mesmo código exatamente.
nos últimos anos, o meu produto SaaS cresceu muito e deu-me a oportunidade de mergulhar cada vez mais fundo em técnicas de otimização para executar minha carga de trabalho o mais eficientemente possível.
Os resultados que vi são impressionantes e ajudaram-me muito a desbloquear o cash flow livre para continuar desenvolvendo minha jornada SaaS.
Neste ponto, o processo PHP dentro do meu produto SaaS está processando mais de 1,2 bilhão (com “b”) de pacotes de dados a cada dia em uma máquina com 2vCPU e 8GB de memória.
Eu usei um grupo de autoescala do AWS para ter mais flexibilidade em caso de picos impredecíveis, mas raramente adiciona uma segunda máquina (uma/duas vezes por semana).
Vamos entrar no tópico do artigo. Acho que você encontrará muito interessante.
O que é Código de Operação PHP? O código de operação PHP significa
operação de código, e refere-se às instruções de nível baixo que são executadas pelo motor PHP após o código fonte PHP que você escreveu ter sido compilado.
Esta é uma representação simples do processo:
Cache de Opcodes PHP
O cache de opcodes do PHP permite economizar três passos no processo de execução do código: análise do código PHP bruto, tokenização e compilação.
Uma vez que os opcodes forem gerados pela primeira vez, eles são armazenados em memória para serem reutilizados nas solicitações subsequentes. Isso reduz a necessidade do motor PHP de recompilar o mesmo código PHP a cada execução, economizando muito consumo de CPU e memória.
O cache de opcodes mais comumente usado no PHP é o OPCache, e está incluído por padrão a partir da versão 5.5 até as versões recentes. Ele é altamente eficiente e amplamente suportado.
O cache do bytecode pré-compilado do script exige que o cache seja invalidado após cada implantação. Isso é necessário porque se arquivos alterados tiverem a versão do bytecode no cache, o PHP continuará executando a versão antiga do código até que você limpe o cache de opcodes, portanto, o novo código será compilado novamente, gerando um novo item de cache.
Como Investigações de Opcode PHP
Para entender como diferentes sintaxes podem impactar o opcode do script, precisamos de uma maneira de pegar o código compilado gerado pelo motor PHP.
Existem duas maneiras de obter o opcode.
Funções Nativas do OPCache
Se você tiver a extensão OPCache habilitada em seu computador, você pode usar suas funções nativas para obter o opcode de um arquivo PHP específico:
// Force compilation of a script
opcache_compile_file(__DIR__.'/yourscript.php');
// Get OPcache status
$status = opcache_get_status();
// Inspect the script's entry in the cache
print_r($status['scripts'][__DIR__.'/yourscript.php']);
VLD (Vulcan Logic Disassembler) PHP Extension
VLD é uma extensão popular de PHP que desmonta o código compilado PHP e emite o opcode. É uma ferramenta poderosa para entender como o PHP interpreta e executa seu código. Assim que instalado, você pode executar um script PHP com VLD ativado usando o comando php
com as opções -d
:
php -d vld.active=1 -d vld.execute=0 yourscript.php
A saída incluirá informações detalhadas sobre o opcode compilado, incluindo cada operação, sua linha de código associada e muito mais.
Use 3v4l (Acrônimo para EVAL)
3v4l é uma ferramenta online muito útil que permite que você veja o opcode gerado por um código PHP que você digita no editor. Basicamente, é um servidor PHP com VLD instalado para pegar a saída de VLD e mostrar o opcode no navegador.
Como é grátis, vamos usar esta ferramenta online para as próximas análises.
Como Gerar Opcode PHP Eficiente
3v4l é perfeito para entender como a sintaxe de código que usamos pode influenciar o opcode PHP resultante de forma boa ou má. Vamos começar a colar o código abaixo em 3v4l. Manter a configuração “todas as versões suportadas” e clicar em “eval”.
namespace App;
strlen('ciao');
Após executar o código, uma barra de menu aparecerá no final. Navegue até a aba VLD para visualizar o opcode correspondente.
line #* E I O op fetch ext return operands
-------------------------------------------------------------------------------------
5 0 E > INIT_NS_FCALL_BY_NAME 'App%5CSpace%5Cstrlen'
1 SEND_VAL_EX 'ciao'
2 DO_FCALL 0
3 > RETURN 1
Note que a primeira operação é INIT_NS_FCALL_BY_NAME
. O interpretador constrói o nome da função usando o namespace do arquivo atual, mas não existe no namespace App\Example
— então como funciona?
O interpretador verificará se a função existe no namespace atual. Se não existir, ele tentará chamar a função correspondente do core.
Aqui temos a oportunidade de dizer ao interpretador para evitar esta verificação dupla e executar diretamente a função do core.
Tente adicionar um backslash (\
) antes de strlen
e clique em “eval”:
namespace App;
\strlen('ciao');
No painel VLD, você agora pode ver o opcode com apenas uma instrução.
line #* E I O op fetch ext return operands
------------------------------------------------------------------------------------- 5 0 E > > RETURN 1
Porque você comunicou a localização exata da função, não há necessidade de considerar nenhuma alternativa.
Se você não quiser usar o backslash, você pode importar a função como qualquer outra classe do namespace raiz:
namespace App;
use function strlen;
strlen('ciao');
Aproveite as Otimizações Automáticas de Opcode
Há também muitos automatismos internos do motor PHP para gerar um opcode otimizado avaliando expressões estáticas em avanço. Esta foi uma das razões mais importantes para o grande melhoramento de desempenho do PHP a partir da versão 7.x.
Ser ciente destas dinâmicas pode realmente ajudar você a reduzir o consumo de recursos e poupar custos. Assim que eu fiz esta pesquisa, comecei a usar estas truques em todo o código.
Deixe-me mostrar um exemplo usando as constantes do PHP. Execute este script no 3v4l:
namespace App;
if (PHP_OS === 'Linux') {
echo "Linux";
}
Veja as duas primeiras linhas do opcode PHP:
line #* E I O op fetch ext return operands
------------------------------------------------------------------------------------- 5 0 E > FETCH_CONSTANT ~0 'App%5CPHP_OS' 1 IS_IDENTICAL ~0, 'Linux' 2 > JMPZ ~1, ->4 6 3 > ECHO 'Linux' 7 4 > > RETURN 1
FETCH_CONSTANT
tenta obter o valor de PHP_OS
do namespace atual e irá procurar no namespace global, já que não existe aqui. Em seguida, a instrução IS_IDENTICAL
executa a instrução IF
.
Agora tente adicionar a barra invertida a uma constante:
namespace App;
if (\PHP_OS === 'Linux') {
echo "Linux";
}
Como você pode ver no opcode, o motor não precisa tentar buscar a constante porque agora está claro onde ela está, e como é um valor estático, já está na memória.
Além disso, a instrução IF
desapareceu porque o outro lado da instrução IS_IDENTICAL
é uma string estática (‘Linux
’), então o IF
pode ser marcado como “true
” sem a sobrecarga de interpretá-lo em cada execução.
É por isso que você tem muito poder para influenciar o desempenho final do seu código PHP.
Conclusão
Espero que tenha sido um tópico interessante. Como mencionei no início do artigo, estou obtendo muitos benefícios ao usar essa tática e, de fato, elas também são utilizadas em nossos pacotes.
Você pode ver um exemplo aqui de como usei essas dicas em nosso pacote PHP para otimizar seu desempenho.
Se você quiser saber mais sobre os desafios de construir uma empresa orientada por desenvolvedores, você pode me seguir no LinkedIn.
Source:
https://dzone.com/articles/php-opcode-improve-application-performance