OPcache e bytecode
LidoNesta aula, você vai entender como o PHP executa seus scripts, o que é o OPcache e como ele armazena o bytecode para acelerar a execução. Vamos explorar a configuração do OPcache e os ganhos reais de performance que ele proporciona, com exemplos práticos e exercícios.
Bem-vindo à aula 78 do nosso curso de PHP! Hoje vamos mergulhar em um dos componentes mais importantes para a performance de aplicações PHP: o OPcache. Se você já se perguntou por que o PHP às vezes parece lento ou como frameworks como Laravel conseguem responder tão rápido, a resposta está, em grande parte, no OPcache. Nesta aula, vamos entender o ciclo de vida de um script PHP, desde o código-fonte até a execução, e como o OPcache otimiza esse processo.
Vamos começar com uma visão geral de como o PHP funciona internamente, depois exploraremos o que é o OPcache, como configurá-lo e, finalmente, os ganhos de performance que você pode esperar. Ao final, você terá um conhecimento sólido para aplicar essas otimizações em seus próprios projetos.
Como o PHP executa
Para entender o OPcache, precisamos primeiro entender o processo de execução de um script PHP. Quando você solicita uma página PHP, o servidor web (como Apache ou Nginx) encaminha a requisição para o interpretador PHP (geralmente o PHP-FPM). O interpretador segue um pipeline de etapas:
- Leitura do código-fonte: O PHP lê o arquivo .php do disco.
- Análise léxica (tokenização): O código é convertido em tokens, que são unidades significativas como palavras-chave, operadores e identificadores.
- Análise sintática (parse): Os tokens são transformados em uma Árvore Sintática Abstrata (AST), que representa a estrutura do código.
- Compilação: A AST é convertida em opcodes, que são instruções de baixo nível que a máquina virtual do PHP (Zend Engine) pode executar.
- Execução: A Zend Engine executa os opcodes, produzindo a saída.
Esse processo acontece a cada requisição, a menos que exista um mecanismo de cache. Para aplicações simples, isso pode ser aceitável, mas em sistemas com alto tráfego, a repetição dessas etapas para cada requisição consome tempo e CPU. É aqui que o OPcache entra.
Vamos ver um exemplo simples de como o PHP transforma código em opcodes. Podemos usar a função opcache_get_status() para inspecionar o cache, mas para visualizar os opcodes, podemos usar a extensão VLD (Vulcan Logic Disassembler) ou ferramentas online. Aqui está um exemplo de código:
<?php
function soma($a, $b) {
return $a + $b;
}
echo soma(2, 3);
?>O PHP tokeniza isso, cria a AST e gera opcodes como ADD e ECHO. O OPcache armazena esses opcodes, evitando que o processo seja refeito.
OPcache
OPcache é uma extensão do PHP que armazena o bytecode compilado (os opcodes) em memória compartilhada. Quando um script é solicitado, o OPcache verifica se o bytecode já existe no cache. Se existir e o arquivo não foi modificado, ele usa o bytecode em cache, pulando as etapas de tokenização, parsing e compilação. Isso reduz drasticamente o tempo de resposta e o uso de CPU.
O OPcache foi introduzido como uma extensão separada (Zend OPcache) e, a partir do PHP 5.5, tornou-se parte integrante do núcleo do PHP. Ele é ativado por padrão em muitas distribuições, mas pode ser necessário habilitá-lo manualmente.
Além de armazenar o bytecode, o OPcache também pode otimizar o código, por exemplo, eliminando opcodes redundantes. No entanto, seu principal benefício é o cache.
Vamos verificar se o OPcache está habilitado no seu ambiente:
<?php
if (extension_loaded('Zend OPcache')) {
echo "OPcache habilitado\n";
print_r(opcache_get_status());
} else {
echo "OPcache não está habilitado\n";
}
?>Quando você executa esse script, você verá informações detalhadas sobre o cache, como o número de hits, misses e o uso de memória.
Configuração
Para configurar o OPcache, você usa o arquivo php.ini ou arquivos de configuração específicos (como opcache.ini). As configurações mais importantes são:
opcache.enable: ativa ou desativa o OPcache (1 ou 0).opcache.memory_consumption: quantidade de memória alocada para o cache (em megabytes). O padrão é 128 MB.opcache.max_accelerated_files: número máximo de arquivos que podem ser armazenados em cache. O padrão é 10000.opcache.validate_timestamps: se ativado (1), o OPcache verifica se o arquivo foi modificado desde o último cache. Isso é útil em desenvolvimento, mas em produção pode ser desativado para evitar overhead.opcache.revalidate_freq: com que frequência (em segundos) o OPcache verifica alterações nos arquivos. O padrão é 2.opcache.enable_cli: ativa o OPcache para scripts CLI (linha de comando). Normalmente desativado, pois scripts CLI são executados uma vez.
Um exemplo de configuração recomendada para produção:
opcache.enable=1
opcache.memory_consumption=128
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.revalidate_freq=0
opcache.enable_cli=0
opcache.save_comments=1
opcache.fast_shutdown=1É importante notar que opcache.validate_timestamps=0 significa que o OPcache não verificará se os arquivos foram alterados, então você precisará recarregar o PHP-FPM sempre que implantar novas versões do código. Em desenvolvimento, você pode manter validate_timestamps=1 e revalidate_freq=2 para que as alterações sejam detectadas rapidamente.
Para aplicar as configurações, você precisa reiniciar o PHP-FPM ou o Apache, dependendo do seu setup. No PHP-FPM, use systemctl restart php-fpm ou service php-fpm restart.
Ganhos
Os ganhos de performance com o OPcache são significativos. Em benchmarks, aplicações PHP com OPcache ativo podem ser de 2 a 3 vezes mais rápidas em termos de throughput (requisições por segundo). Isso porque a etapa de compilação é a mais custosa do ciclo de vida do PHP.
Além disso, o OPcache reduz o uso de CPU, pois as etapas de tokenização e parsing são eliminadas. Isso permite que servidores atendam mais requisições com os mesmos recursos.
Vamos fazer um teste simples para ilustrar. Crie um arquivo teste.php com um loop pesado:
<?php
$inicio = microtime(true);
for ($i = 0; $i < 100000; $i++) {
$x = sqrt($i);
}
echo "Tempo: " . (microtime(true) - $inicio) . " segundos\n";
?>Execute esse script várias vezes com OPcache desativado e depois ativado, e compare os tempos. Você verá que a primeira execução pode ser mais lenta (porque o OPcache precisa compilar e armazenar), mas as execuções subsequentes serão muito mais rápidas.
Em aplicações reais, como WordPress ou Laravel, o OPcache é essencial para escalar. Muitos provedores de hospedagem já o habilitam por padrão, mas é importante verificar e ajustar as configurações para seu caso de uso.
Boas práticas
Aqui estão algumas boas práticas ao usar OPcache:
- Em produção, desative
validate_timestampspara evitar a verificação de arquivos a cada requisição. - Ajuste
memory_consumptionemax_accelerated_filesde acordo com o tamanho do seu aplicativo. Monitore o uso de memória comopcache_get_status(). - Use
opcache.revalidate_freqem desenvolvimento para que as mudanças sejam detectadas rapidamente. - Recarregue o PHP-FPM após implantar novas versões do código quando
validate_timestamps=0. - Considere o uso de JIT (Just-In-Time) em PHP 8, que pode ser combinado com OPcache para melhorias adicionais.
Exercícios
- Explique o pipeline de execução do PHP e onde o OPcache atua.
- Qual é a diferença entre opcache.validate_timestamps=1 e 0? Em que cenário cada um é recomendado?
- Escreva um script PHP que exiba o status do OPcache, incluindo o número de hits e misses.
- Configure o OPcache em um ambiente de teste com as seguintes diretivas: memory_consumption=64, max_accelerated_files=5000, validate_timestamps=0. Descreva como você faria e quais os efeitos esperados.
- Dado o código a seguir, quantas vezes ele será compilado se o OPcache estiver ativo e o arquivo não for modificado? Justifique.
<?php
if (function_exists('opcache_get_status')) {
$status = opcache_get_status();
echo "Hits: " . $status['opcache_statistics']['hits'] . "\n";
echo "Misses: " . $status['opcache_statistics']['misses'] . "\n";
} else {
echo "OPcache não disponível\n";
}
?>