Back-End

16 abr, 2012

Dicas de desempenho do Python – Parte 02

Publicidade

É útil lembrar que o código compilado estaticamente ainda importa. Só para citar alguns, Chrome, Firefox, MySQL, MS Office e Photoshop são softwares altamente otimizados que muitos de nós estão usando diariamente. O Python, como uma linguagem interpreted, não é alheio a esse fato. O Python por si só não pode cumprir os requisitos em alguns domínios onde a eficiência é uma prioridade principal. É por isso que o Python suporta infraestrutura que chega ao nível mais baixo de hardware para você, delegando o trabalho pesado para as linguagens rápidas, como C. Esse é um recurso crítico para computação de alto desempenho e programação incorporada. No artigo Dicas de desempenho do Python – Parte 01, discutimos como usar o Python de forma eficaz. Na parte 02, vamos abordar o perfil e a extensão Python.

1. Em primeiro lugar, resista à sua tentação de otimização.

A otimização irá introduzir complexidade à sua base de código. Verifique a lista a seguir antes de integrar com outras linguagens. Se a sua solução é “suficientemente boa”, otimizar não é necessariamente uma coisa boa.

  1. Os seus clientes relatam problemas de velocidade?
  2. Você pode minimizar o acesso I/O aao disco rígido?
  3. Você pode minimizar o acesso I/O à rede?
  4. Você pode atualizar o seu hardware?
  5. Você está escrevendo bibliotecas para outros desenvolvedores?
  6. O seu software terceirizado está atualizado?

2. Código de perfil com ferramentas, não com intuição.
O problema de velocidade pode ser sutil, então não confie na intuição. Graças ao módulo “cprofiles”, você pode fazer o perfil do código Python, simplesmente executando “python -m cProfile myprogram.py”.

Nós escrevemos um programa de teste (à direita). O resultado de perfil está na caixa preta (acima). O gargalo aqui é a chamada de função “very_slow ()”. Podemos ver também que “fast ()” e “slow ()” foram chamadas 200 vezes cada uma. Isso significa que, se pudermos melhorar “fast ()” e “slow ()”, teremos um aumento decente no desempenho. O módulo cprofiles também pode ser importado durante o tempo de  execução. Isso é útil para verificar processos de longa duração.


 
3. Reveja a complexidade do tempo de execução
Depois do profiling, faça uma análise básica de quão rápida é a sua solução. Constante de tempo é o ideal. Algoritmo logaritmo é estável. Solução fatorial não irá dimensionar.
O(1) -> O(lg n) -> O(n lg n) -> O(n^2) -> O(n^3) -> O(n^k) -> O(k^n) -> O(n!)

4. Use pacotes terceirizados
Existem muitas bibliotecas de alto desempenho e ferramentas projetadas para Python. Aqui está uma pequena lista útil de pacotes de aceleração.

1. NumPy: um programa open source equivalente ao MatLab
http://numpy.scipy.org

2. SciPy: outra biblioteca de processamento numérico rápido
http://scipy.org

3. GPULib: use seus GPUs para acelerar seu código
http://txcorp.com/products/GPULib

4. PyPy: compilador JIT para otimizar seu código Python
http://codespeak.net/pypy

5. Cython: traduz código Python para C
http://cython.org

6. ShedSkin: traduz Código Python para C++
http://code.google.com/p/shedskin

 
5. Utilize módulo de multiprocessamento para verdadeira concorrência.
Porque GIL (Global Interpreter Lock) irá serializar threads, multi-threading em Python não pode acelerar seu código em máquinas com múltiplos processadores, nem em cluster de máquinas. Portanto, o Python oferece um módulo de multiprocessamento que pode gerar processo extra em vez de threads, elevando a limitação imposta pelo GIL. Além disso, você pode combinar esta dica com o código externo C, para tornar o seu programa ainda mais rápido.

Observe que um processo é geralmente mais dispendioso que uma thread, uma vez que threads automaticamente compartilham o mesmo espaço de endereçamento de memória e descritores de arquivos. Isso significa que vai demorar mais tempo para se criar um processo, e provavelmente custará mais memória do que a criação de uma thread. Isso é algo a considerar se você pretende gerar muitos processos.

 
6. Vamos ao nativo.
Então você decidiu usar código nativo para o desempenho. Com o módulo padrão ctypes, você pode carregar diretamente do binário compilado (.dll ou arquivos .so) para o Python sem se preocupar em escrever código em C/C++ ou construir dependências. Por exemplo, nós escrevemos um programa (à direita) que carrega libc para gerar números aleatórios. No entanto, a sobrecarga de ligação ctypes não é leve. Você deve considerar ctypes como uma colagem rápida para as suas bibliotecas de sistema operacional ou drivers de dispositivo para acessar o hardware. Existem várias bibliotecas, como SWIG, Cython, e Boost Python, que introduzem muito menos chamando overhead do que o ctypes faz. Aqui nós escolhemos a biblioteca Boot Python para interoperar com Python, porque Boost Python suporta recursos orientados a objetos, tais como classe e herança. Como podemos ver no exemplo (à direita), mantemos o código habitual C ++ (linha 1 ~ 7), e então o importamos mais tarde (linha 21 ~ 24). O trabalho principal aqui é escrever um wrapper (linha 10 ~ 18).

 

Conclusão

Espero que a série sobre o desempenho do Python tenha feito de você um desenvolvedor melhor. Por fim, gostaria de salientar que, enquanto forçar o desempenho até o limite é um jogo divertido, a otimização prematura poderia transformá-lo em uma caricatura. O Python permite a você interagir com linguagens compatíveis com C sem problemas, mas prefere desenvolvimento de velocidade em relação à execução de velocidade por um bom motivo. Você tem que se perguntar se os usuários pagariam mais pelo seu trabalho duro em horas de otimização. Além disso, eliminar a leitura do código apenas para economizar alguns milissegundos quase nunca funciona. Os desenvolvedores em sua equipe serão sempre gratos por você escrever um código limpo.

?
Texto original da equipe Monitis, liderada por Hovhannes Avoyan, disponível em http://blog.monitis.com/index.php/2012/03/21/python-performance-tips-part-2/