Pular para o conteúdo
Código e Café com PauloDev: Explorando o Universo da Programação

Código e Café com PauloDev: Explorando o Universo da Programação

Explore o universo da programação e tecnologia com PauloDev. Descubra insights, tutoriais e inovações que moldam o futuro digital. De linhas de código a ideias revolucionárias, embarque nesta jornada tech onde a paixão pela inovação se encontra com a expertise do desenvolvimento. Seja bem-vindo ao blog que transforma códigos em narrativas de vanguarda, guiado pela visão única de PauloDev no vasto panorama tecnológico.

  • Meu portifolio

Melhorando a performance dos seus projetos PHP, conheça o OPCache e o JIT

10 de dezembro de 2024
Por Paulo In Boas Práticas

Melhorando a performance dos seus projetos PHP, conheça o OPCache e o JIT

Provavelmente, se você acompanha algum canal sobre alguma linguagem de programação deve ter visto recentemente o “1 Billion nested loop iterations”, uma publicação que busca mostrar o tempo de resposta de algumas linguagens de programação ao percorrer esses 1 bilhão de dados.

Algo bastante interessante, e que faz nós, como programadores, pensar em maneiras de melhorar a performance da nossa linguagem, e é isso que quero falar hoje. Quero apresentar para vocês o amigo dos devs PHP quando o assunto é dar aquele up na performance de grandes aplicações, estou falando do OPCache e do JIT que são funcionalidades que tem como objetivo melhorar o desempenho na execução de scripts PHP.

OPCache

Começando pela base, o OPCache é uma extensão para o PHP que melhora o desempenho das aplicações ao armazenar o bytecode dos scripts PHP em um cache na memória compartilhada do servidor. Apesar de ter ganhado força ultimamente, principalmente por conta das versões 8.* do PHP, o OPCache foi introduzido inicialmente como parte do PHP na versão 5.5, porém, ele era uma extensão separada conhecida como “Zend Optimizer+”.

Como o compilador do PHP funciona?

Por padrão, quando o processo de execução de um script PHP é iniciado, o servidor vai ler o arquivo .php do disco, e em seguida iniciar o “Parsing e Tokenização” que basicamente, pega o código PHP, análise ele e o transforma em tokens. Em seguida, começa a etapa de compilação desses tokens, que são convertidos em bytecode, que são uma representação intermediária (entre o código-fonte PHP e o código de máquina executado pela CPU) otimizada do código PHP, esse bytecode é o que vai ser executado pela Zend Engine (engine padrão do PHP).

Esse processo por sua vez, é executado toda vez que um script é chamado, mesmo que o código-fonte do script não tenha mudado nada, o que, por sua vez, consome recursos do servidor, especialmente em aplicações que envolvem muitos scripts PHP.

Certo, e como o OPCache otimiza esse processo?

Chegamos no OPCache, basicamente, ele intervém nesse processo padrão armazenando o bytecode compilado do seu script PHP na memória compartilhada, só que, na primeira execução do seu script, o PHP vai seguir essa etapa padrão, gerando o bytecode, e o OPCache armazenando ele, contudo, nas próximas execuções o PHP irá verificar se o script já tem um bytecode armazenado em cache, e caso ele exista, ele é carregado diretamente da memória, evitando o processo repetido.

Como benefícios, o OPCache traz consigo uma grande diminuição no tempo de execução do seu projeto, já que o bytecode será buscado diretamente da memória. O que por sua vez, diminui o consumo da CPU, já que o processo de parsing e compilação são evitadas durante esse processo, e claro, trazendo uma consistência maior para o seu código, já que os scripts cacheados são executados de forma consistente, independentemente de variaçÕes no desempenho do sistema de arquivos.

Então, como faço para ativar o OPCache?

Para ativá-lo é bem simples, você precisa apenas ir no seu arquivo php.ini e configurar as seguintes linhas:

; Ativa o OPCache
opcache.enable=1

; Tamanho máximo da memória compartilhada alocada para o cache (em MB)
opcache.memory_consumption=128

; Quantidade de memória reservada para strings internadas (em MB)
opcache.interned_strings_buffer=8

; Número máximo de arquivos que podem ser armazenados no cache
opcache.max_accelerated_files=10000

; Valida se o script foi alterado antes de executar (1 para ambiente de desenvolvimento)
opcache.validate_timestamps=1

; Intervalo em segundos para verificar alterações nos scripts (0 para produção)
opcache.revalidate_freq=2

OBS: Em ambientes de produção, é recomendado desativar a validação constante de scripts para maior desempenho:

opcache.validate_timestamps=0
opcache.revalidate_freq=0

Um ponto que você deve ficar alerta nessas configurações, é caso seus scripts tenham uma atualização recorrente, pois caso um script PHP for modiciado, ele não será atualizado no cache até que o OPCache o detecte (baseado em validade_timestamps), ou caso você limpe o cache manualmente.

Agora que você entende o que é o OPCache, o que é o JIT?

Apesar do nome, não, não estamos falando do sistema de administração Just-in-Time, porém, ambos seguem o mesmo princípio, acelerar processos.

O JIT (Just-In-Time compilation) é uma técnica de compilação dinâmica que converte o bytecode PHP diretamente em código de máquina nativo (assembly), o que é diferente do que foi citado anteriormente, já que o PHP interpreta o bytecode gerado a partir do código-fonte. Isso permite que o seu código seja executado diretamente pela CPU, sem a necessidade de interpretação contínua pela Zend Engine. Essa abordagem também pode ser vista em outras linguagens modernas, como o JavaScript (V8), Java (JVM) e até mesmo o Python (PyPy), para melhorar o desempenho.

Certo, e como o JIT funciona no PHP?

Basicamente, sem o PHP no seu fluxo tradicional sem o JIT, converte o código-fonte do seu script em bytecode pelo compilador da Zend Engine (como falamos anteriormente), e esse bytecode é interpretado pela Zend Engine, instrução por instrução.

Com o JIT o jogo muda, o bytecode é analisado durante a execução, e partes do código são que são executadas frequentemente (como loops ou cálculos intensivos) são compiladas para código de máquina nativo, que é executado diretamente pela CPU, sem precisar passar pela Zend Engine.

O JIT no PHP funciona em conjunto com o OPCache, que já armazena o bytecode em cache. Enquanto o OPCache melhora a reutilização do bytecode, o JIT vai além, gerando código de máquina otimizado.

Como configurar o JIT?

O JIT possui várias maneiras de ser configurado através da opção opcache.jit no seu php.ini. Os principais modos incluem:

  • Desativado (opcache.jit=0): O PHP funciona sem o JIT, usando apenas o OPCache para armazenar o bytecode.
  • Tracing JIT (opcache.jit=1255): É o modo mais avançado e recomendado. Ele rastreia partes do código frequentemente executadas (hot paths) e as otimiza gerando código nativo.
  • Function JIT (opcache.jit=1205): Compila funções inteiras para código nativo.
  • Compile Time JIT: Compila todo o bytecode em código nativo logo na inicialização, mas geralmente não é usado devido ao alto custo inicial.

Vale se atentar que o JIT acaba se limitando para aplicações web tradicionais. A maioria dos aplicativos PHP (baseados em frameworks como Laravel ou até mesmo CMS como WordPress) não experimenta grandes ganhos de desempenho com o JIT. Isso ocorre justamente porque esses sistemas geralmente passam mais tempo em I/O (banco de dados e redes) do que em execução intensiva de CPU. Portanto, caso você não vá usar intensamente a CPU durante a execução dos seus scripts, não tem a necessidade de habilitar o JIT. Um bom uso seria em scripts que realizam cálculos matemáticos intensivos, processamento de imagens ou vídeo ou casos mais extremos como simulações científicas ou processamento de dados em tempo real.

A configuração no seu php.ini ficaria assim, por exemplo:

; Ativa o OPCache e define o buffer para o JIT
opcache.enable=1
opcache.jit_buffer_size=100M

; Configura o modo do JIT (Tracing JIT, recomendado)
opcache.jit=1255

Melhorando a execução do nosso script

Já que iniciei mostrando o “1 Billion nested loop iterations”, nada melhor que darmos um up a execução do nosso script. Ali estamos com 9.93s de execução do PHP, não sei exatamente como realizaram esse teste, mas estou supondo que rodaram apenas um for de 1 até 1 bilhão. Minhas configurações ficaram assim:

Estrutura das pastas

project/
├── docker-compose.yml
├── Dockerfile
├── php/
│   ├── index.php
│   └── php.ini

docker-compose.yml

version: '3.8'

services:
  php:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ./php:/var/www/html:ro 
    ports:
      - "8080:80"

Dockerfile

# Base image com PHP 8.4 e Apache
FROM php:8.4-apache

# Instalação de pacotes adicionais e o OPCache
RUN docker-php-ext-install opcache

# Copia o php.ini para o diretório correto de configurações
COPY ./php/php.ini /usr/local/etc/php/conf.d/30-custom.ini

# Ativar o JIT e configurar o OPCache no php.ini
RUN echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/30-opcache.ini
RUN echo "opcache.jit_buffer_size=100M" >> /usr/local/etc/php/conf.d/30-opcache.ini
RUN echo "opcache.jit=1255" >> /usr/local/etc/php/conf.d/30-opcache.ini

# Configuração básica do Apache para servir o PHP
RUN a2enmod rewrite

index.php

<?php
$inicio = microtime(true);

for ($i = 1; $i <= 1000000000; $i++) {
}

$fim = microtime(true);

$tempoExecucao = $fim - $inicio;

echo "O tempo de execução foi de " . number_format($tempoExecucao, 4) . " segundos.";



php.ini
; Configurações básicas do PHP
display_errors=On
error_reporting=E_ALL

; Configurações do OPCache
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=10000
opcache.validate_timestamps=0
opcache.jit_buffer_size=100M
opcache.jit=1255

Por fim, basta rodar o docker-compose up –build para subir seu projeto e acessar http://localhost:8080/index.php no seu navegador. Aqui obtive um ganho até que considerável de 86.16%, caimos de 9.93s para 1.37s

Por fim, fique à vontade para testar as configurações nesse projeto, às vezes você consegue até um nº melhor na sua máquina.

Promoção
Written by:

Paulo

Ver todos os posts

Categorias

  • Android
  • Android Studio
  • Angular
  • API
  • AWS
  • Back-end
  • Bash
  • Boas Práticas
  • CSharp
  • CSS
  • Django
  • Docker
  • Electron
  • Front-end
  • Git
  • Github
  • Html
  • Http
  • Java
  • JavaScript
  • Laravel
  • Linha de comando
  • Linux
  • Machine Learning
  • Metodologias
  • Mysql
  • Node
  • NoSql
  • PHP
  • Power Shell
  • Python
  • Segurança
  • Sem categoria
  • SQL
  • Tecnologia
  • Testes
  • VueJs
  • Windows

Últimos posts

  • Python para o desenvolvimento Web: Como o Django pode alavancar na escrita do seu código
  • Conheça as Transactions e como elas podem te ajudar a testar o seu sistema
  • Melhorando a performance dos seus projetos PHP, conheça o OPCache e o JIT
  • Redis com Laravel: Uma ferramenta poderosa para o escalonamento horizontal da sua aplicação
  • Conhecendo e configurando um servidor de Load Balance com YARP e NGINX

© Todos os direitos reservados PauloDev 2023