Lançado Adianti Framework 7.6!
Clique aqui para saber mais
5 dicas de desempenho ao desenvolver com Adianti Framework e PHP Neste artigo vou mostrar cinco dicas úteis para aumentar o desempenho em aplicações desenvolvidas em PHP com o Adianti Framework. 1. Usar sempre a última versão do PHP O PHP deu um grande salto de performance nas últimas versões. Manter a versão de desenvolvimento e consequentemente a de produção atualizadas, vão garantir que possamos continuamente nos beneficiar dessas me...
PD
5 dicas de desempenho ao desenvolver com Adianti Framework e PHP  
Fechado
Neste artigo vou mostrar cinco dicas úteis para aumentar o desempenho em aplicações desenvolvidas em PHP com o Adianti Framework.

1. Usar sempre a última versão do PHP


O PHP deu um grande salto de performance nas últimas versões. Manter a versão de desenvolvimento e consequentemente a de produção atualizadas, vão garantir que possamos continuamente nos beneficiar dessas melhorias. Porém, sempre é importante lembrar que existem eventuais ajustes a serem feitos no código-fonte (práticas que já vinham sendo notificadas via DEPRECATED) que para atualizar agora tornam-se essenciais. Nos links a seguir, temos os passos para migração desde o 5.3 até o 7.0. Todas as sessões são importantes, principalmente "Backward Incompatible Changes", que são mudanças que provocam incompatibilidades:
php.net/manual/pt_BR/migration54.php
php.net/manual/pt_BR/migration55.php
php.net/manual/pt_BR/migration56.php
php.net/manual/pt_BR/migration70.php

Geralmente as mudanças são em poucas funções. Em contrapartida, as vantagens são grandes. O link a seguir mostra um benchmark no qual o tempo de processamento caiu em média de 3.9s (php-5.3) para 2.3s (php-5.6) somente com a atualização do PHP:
www.lornajane.net/posts/2014/php-5-6-benchmarks

Já este outro artigo mostra um benchmark no qual uma instalação e Workpress com php-7 responde 604 req/seg, enquanto a mesma com php-5.3 responde 213 req/seg:
https://kinsta.com/blog/hhvm-vs-php-7/

Já nesta outra apresentação, realizada pelo Rasmus Lerdorf, aponta que o php-7 deve apresentar um desempenho médio cerca de 100% superior que o php-5.6 na maioria das aplicações:
talks.php.net/velocity15#/php7

Além disso, este outro slide (#21) aponta uma redução no consumo de memória em torno de 50% entre o php-5.3 e o php-5.6:
pt.slideshare.net/wimg/the-why-and-how-of-moving-to-php-55-shorter

Por fim, este infográfico apresenta alguns saltos de desempenho entre o php5.6 e o php-7:
www.zend.com/en/resources/php7_infographic


2. Usar cache de arquivos com opcache


Um dos recursos que provocou grande melhoria no desempenho do php foi sem dúvidas o cache de arquivos. No lugar de realizar o carregamento (require, include) dos arquivos à cada requisição, os scripts são mantidos pré-processados e compilados em memória, otimizando muito a execução do programa. Este recurso passou a ser distribuído nativamente a partir do php-5.5:

Para carregar a biblioteca, é necessário definir "zend_extension" no php.ini, ou em
algum arquivo carregado a partir dele:
zend_extension=opcache.so


Obs: Se for compilar o PHP na mão (configure, make, make install), é importante adicionar ao final do configure (--enable-opcache).


O cache de arquivos possui as seguintes configurações específicas no php.ini:
[opcache] opcache.enable=0 opcache.memory_consumption=128 opcache.interned_strings_buffer=8 opcache.max_accelerated_files=4000 opcache.revalidate_freq=60 opcache.fast_shutdown=1 opcache.enable_cli=1



Maiores informações em:
php.net/manual/pt_BR/opcache.installation.php

O cache de arquivos melhora o desempenho da aplicação de maneira consistente e perceptível, mas é importante percebermos que a requisição de scripts é uma operação ínfima se comparada à outras, como a busca de dados em bases de dados relacionais (SQL), por exemplo. No próximo tópico abordaremos isso.



3. Usar cache de objetos com APC ou Redis


Como vimos no tópico anterior, o cache de arquivos é importante, mas o grande salto de desempenho está no cache de dados (objetos).

O cache de objetos no Adianti funciona por que utilizamos o padrão de projeto Active Record. Assim, cada registro do banco de dados é tratado como um objeto em memória. O framework possui de maneira nativa uma classe chamada TAPCache, que se comunica com o APC do PHP. Para quem não sabe, o APC permite armazenar variáveis em memória RAM. O funcionamento se dá como exposto na figura a seguir. Sempre que um objeto é gravado pelo método store(), ele é gravado tanto no banco de dados (a), quanto no cache (d). Agora, sempre que um objeto for requisitado por meio da instanciação direta (Ex: new Estado(10)), ou indireta ($matricula->aluno->cidade->nome), primeiro o framework verifica se o objeto está na memória RAM (c). Caso encontrado, ele é disponibilizado imediatamente para a aplicação. Caso ele não esteja na memória RAM (c), então ele é carregado do banco de dados (b), e gravado na memória RAM (d), para então ser disponibilizado para a aplicação.

Funcionamento do cache de objetos


Este recurso vem desabilitado por padrão. Mas para ligar é muito simples. Em primeiro lugar é preciso ter o APC instalado (apt-get install php5-apcu). E em seguida, ligar o cache nas classes que você deseja manter em memória. É importante lembrar que os objetos são carregados no cache na medida em que são acessados pela primeira vez. Veja a linha com a definição da constante CACHECONTROL. Você poderá usar a classe nativa TAPCache, ou indicar ali outra classe para manipular o cache. Você poderá implementar uma classe que armazena os dados em Memcached, por exemplo. A classe indicada deverá implementar a interface AdiantiRegistryInterface.
  1. <?php
  2. class Customer extends TRecord
  3. {
  4.     const TABLENAME    'customer';
  5.     const PRIMARYKEY   'id';
  6.     const IDPOLICY     'max'// {max, serial}
  7.     const CACHECONTROL 'TAPCache';
  8. }
  9. ?>


Mais referências em: www.adianti.com.br/forum/pt/view_1341?banco-de-dados-em-memoria-nao-

Algumas observações importantes:
- Os dados são carregados para o cache na medida em que são acessados (aos poucos);
- Você pode fazer um script para dar um loop em alguma classe e "forçar" a carga de toda uma tabela de vez;
- Se você mexer no registro do banco de dados, ele não estará atualizado no cache. Nesse caso, é melhor limpar o cache (reinicie o apache);
- Não armazene todas tabelas em RAM, você não terá RAM suficiente para isso. Escolha tabelas com até uma determinada quantidade de registros, faça cálculos.

O APC não é o único mecanismo de cache. Outra boa solução é o Redis (redis.io). Em breve, teremos uma classe para realizar comunicação com Redis.


4. Use sessões em RAM


Sempre que armazenamos dados em sessão ($_SESSION no PHP nativo, TSession::setValue() no Adianti Framework), os dados da sessão por default são armazenadas de maneira serializada em um arquivo no lado do servidor em pastas como /tmp ou /var/lib/php5/sessions, sendo que o caminho pode variar conforme a instalação do sistema operacional. Na dúvida, rode um phpinfo(); e busque pela variável "session.save_path" para descobrir onde os arquivos de sessão estão localizados.

Sempre que precisamos acessar o conteúdo da sessão, é realizada a leitura do arquivo serializado a partir do disco. Entretanto o acesso à disco é lento (claro que isso depende da tecnologia usada), mas em linhas gerais, a memória RAM é sempre mais rápida que o armazenamento em disco. Então neste tópico vamos aprender a armazenar as sessões em um diretório mapeado na memória RAM no Linux.

Os primeiros comandos criam o diretório e montam ele usando tmpfs.

mkdir /var/lib/ramdisk chmod 777 /var/lib/ramdisk mount -t tmpfs -o size=512M tmpfs /var/lib/ramdisk/


Adicione isso ao /etc/fstab (para ficar permanente)
tmpfs /var/lib/ramdisk tmpfs size=512M,atime 0 0


Em seguida, modifique a variável "session.save_path" para apontar para o diretório criado:
session.save_path="/tmp/ramdisk"


Pronto. Existem outras soluções usando Memcached ou Redis, mas essa solução é simples e rápida. Além disso, este post (stackoverflow.com/a/6022478) reforça que o acesso local (file/socket/pipe) sempre é mais rápido que o acesso à um socket de rede. Antes de adotar essa solução, não esqueça de verificar se você realmente possui memória RAM disponível para tal.

Obs: Para Windows tem a solução de usar Shared Memory. Mas não ninguém deveria rodar PHP no Windows, não é mesmo? Ele foi feito para rodar em ambientes Unix-like.
Referências:
https://amigotechnotes.wordpress.com/2014/04/06/improve-php-session-performance-by-utilizing-ram/
kvz.io/blog/2011/04/29/faster-php-sessions/

5. Carga direta de arquivos


As classes da aplicação (subdiretório app no Adianti Framework) são localizadas dinamicamente a partir da estrutura de diretórios, o que pode tornar-se oneroso caso a estrutura de diretórios seja muito grande. Geralmente em ambientes Unix-like isso não é um problema, pois estes são bastante performáticos. Mas é possível indicarmos para o framework buscar as classes diretamente nos arquivos, sem precisar localizá-los. Para tal, basta criarmos no diretório raíz da aplicação um arquivo chamado map.php, com o seguinte conteúdo:

map.php
  1. <?php
  2. AdiantiCoreLoader::setClassPath('CustomerDataGridView',
  3.     'app/control/Organization/ComplexViews/CustomerDataGridView.class.php');
  4. AdiantiCoreLoader::setClassPath('CustomerFormView',
  5.     'app/control/Organization/ComplexViews/CustomerFormView.class.php');
  6. ?>


Neste arquivo, indicamos para cada classe sua exata localização. Agora precisamos executar o arquivo map.php logo após o carregamento do init.php dentro do engine.php:
  1. <?php
  2. require_once 'init.php';
  3. require_once 'map.php';
  4. ?>


Pronto, agora as classes serão localizadas diretamente a partir dos arquivos, não mais localizadas dinamicamente.

Outras técnicas importantes:
Neste artigo abordamos principalmente técnicas relativas ao desempenho na camada da aplicação. Porém, tão importante quanto preocupar-se com o desempenho da aplicação é preocupar-se com o desempenho do banco de dados. Quem nunca deparou-se com uma aplicação lenta quando na verdade só faltavam criar índices de busca?

Pois bem, aqui vão alguns links sobre ajustes de performance em PostgreSQL:
www.revsys.com/writings/postgresql-performance.html
leopard.in.ua/2013/09/05/postgresql-sessting-shared-memory/
https://wiki.postgresql.org/wiki/Tuning_Your_PostgreSQL_Server
www.postgresql.org/docs/9.4/static/runtime-config-resource.html

Curso completo Meu Negócio Pronto
Use para si, ou transforme em um negócio: Inclui aulas e códigos-fontes
Gestor de conteúdo (SITE) + Loja Virtual (E-Commerce) + Emissor de Notas para infoprodutos


Meu negócio pronto Quero me inscrever agora!

Comentários (3)


DM

Dicas de otimização do MYSQL seria muito bem vinda
RK

Pablo,

Ao que se refere ao OPCache, a variável "opcache.enable" não deveria estar em "1" para que o módulo seja habilitado?
RK

Pablo,

Sobre usar sessões em RAM, na variável "session.save_path" você indicou o valor "/tmp/ramdisk", sendo que o correto seria "/var/lib/ramdisk".
Caso a pessoa venha a utilizar o famoso "copy and paste" vai dar errado!