Performance e otimização

Este documento fornece uma visão geral das técnicas e ferramentas que podem ajudá-lo a rodar o seu código Django com maior eficiência - mais rápido, e usando menos recursos do sistema.

Introdução

Geralmente a primeira preocupação que surge é escrever código que funcione, no qual a lógica trabalhe de acordo com o que foi definido para produzir a saída esperada. Algumas vezes, entretanto, isso não será o suficiente para fazer o código funcionar de modo tão eficiente quanto seria o desejado.

Nesse caso, é preciso algo - e na prática, geralmente uma porção de coisas - para melhorar a performance do código sem, ou ao menos minimamente, afetar o seu comportamento.

Abordagens Comuns

O que exatamente você está otimizando?

É importante ter uma ideia clara do que você entende por ‘performance’. Não existe uma métrica única para ela.

Melhorar a velocidade pode ser o objetivo mais óbvio para um programa, mas algumas vezes outras melhorias de performance podem ser desejadas, tais como menor consumo de memória ou menor consumo de recursos de banda da rede ou do banco de dados.

Melhorias em uma área geralmente irão proporcionar melhor performance em outras, mas nem sempre; algumas vezes uma pode vir em detrimento das outras. Por exemplo, uma melhoria na velocidade de execução de um programa pode fazê-lo usar mais memória. Até pior, ela pode ser contraproducente - Se a melhoria na velocidade de execução consumir tanta memória ao ponto de o sistema começa a ficar sem ela, você terá causado mais mal que bem.

Existem outras escolhas para ter em mente. O seu próprio tempo é um recurso valioso, mais precioso do que o tempo de CPU. Algumas melhorias podem ser muito difíceis para valer a pena sua implementação, ou podem afetar a portabilidade ou manutenibilidade do seu código. Nem todas as melhorias de performance valem o seu esforço.

Portanto, você precisa saber quais melhorias de performance você está querendo obter, e você também precisa ter uma boa razão para caminhar nessa direção - e para tanto você precisa:

Performance benchmarking

Não é bom assumir ou simplesmente chutar os locais de ineficiência do seu código.

Ferramentas do Django

django-debug-toolbar is a very handy tool that provides insights into what your code is doing and how much time it spends doing it. In particular it can show you all the SQL queries your page is generating, and how long each one has taken.

Painéis de terceiros também estão disponíveis para a barra de ferramentas, que pode (por exemplo) emitir relatórios sobre a performance do cache e tempos de renderização dos templates.

Serviços de terceiros

Existem vários serviços gratuitos que podem analisar e emitir relatórios de performance das páginas do seu site sob a perspectiva de um cliente remoto HTTP, simulando assim a experiência de um usuário de verdade.

Esses serviços não podem avaliar as partes internas do seu código, mas podem fornecer uma melhor visão da performance geral do seu site, incluindo aspectos que não podem ser medidos adequadamente de dentro de um ambiente Django. Exemplos incluem:

Existem também vários outros serviços pagos que fazem uma análise similar, incluindo alguns capazes de identificar o Django e que podem se integrar com o seu código para diagnosticar sua performance de forma bem mais abrangente.

Get things right from the start

Some work in optimization involves tackling performance shortcomings, but some of the work can be built-in to what you’d do anyway, as part of the good practices you should adopt even before you start thinking about improving performance.

A esse respeito Python é uma excelente linguagem para se trabalhar, porque soluções que parecem elegantes e mais adequadas geralmente são também as com melhor performance. Como em muitos trabalhos, aprender o que parece correto leva tempo, mas uma das regras mais úteis é:

Work at the appropriate level

O Django oferece muitas formas diferentes de se resolver um problema, mas só porque é possível fazer algo de uma certa maneira não significa que ela seja a maneira apropriada para resolvê-lo. Por exemplo, você pode acabar descobrindo que é possível calcular a mesma coisa - o número de itens em uma coleção, talvez - em um QuerySet, em Python, ou em um template.

Porém, quase sempre será mais rápido fazer isso em leveis mais baixos do que em leveis mais altos. Em leveis mais altos o sistema tem que lidar com objetos através de múltiplos níveis de abstração e maquinário.

Isto é, o banco de dados tipicamente faz coisas mais rápido do que o Python consegue fazer, que por sua vez pode fazê-las mais rápido do que a linguagem de templates.

# QuerySet operation on the database
# fast, because that's what databases are good at
my_bicycles.count()

# counting Python objects
# slower, because it requires a database query anyway, and processing
# of the Python objects
len(my_bicycles)
<!--
Django template filter
slower still, because it will have to count them in Python anyway,
and because of template language overheads
-->
{{ my_bicycles|length }}

De modo geral, o level mais apropriado para o trabalho é o level mais baixo que alguém está disposto a programar.

Nota

O exemplo acima é meramente ilustrativo.

Primeiramente, em um caso real você precisaria considerar o que está acontecendo antes e depois da contagem para trabalhar numa maneira otimizada de fazê-la nesse contexto em particular. A documentação sobre otimização do banco de dados descreve um caso onde a contagem no template seria melhor.

Em segundo lugar, existem outras opções para considerar: em um caso real, {{ my_bicycles.count }}, que invoca o método QuerySet count() diretamente do template, pode ser a escolha mais apropriada.

“Caching” – cacheando

Com frequência ele é custoso (isto é, lento e faminto por recursos) para calcular o valor, então podem existir grandes benefícios em salvá-lo em um cache acessível rapidamente, pronto para a próxima vez em que ele for solicitado.

É uma técnica suficientemente poderosa e significante que o Django inclui em um vasto framework para caches, assim como outras pequenas peças de funcionalidades envolvendo caches.

O framework de “cache” do Django

O framework de “cache” do Django oferece oportunidades muito significativas de ganhos de performance, salvando conteúdo dinâmico de modo que ele não precise ser calculado a cada requisição.

Para conveniência, Django oferece diferentes níveis de granularidade de cache: você pode fazer cache da saída de views específicas, ou somente de partes difíceis de se produzir, ou mesmo de um site inteiro.

Implementing caching should not be regarded as an alternative to improving code that’s performing poorly because it has been written badly. It’s one of the final steps toward producing well-performing code, not a shortcut.

O decorator cached_property

É comum ter que chamar um método de instância de uma classe mais de uma vez. Se essa função for custosa, fazer isso pode ser um desperdício.

Usando o decorator cached_property o valor retornado é salvo por uma property; a próxima vez que a função é chamada nessa instância, ela irá retornar o valor salvo ao invés de calculá-lo novamente. Repare que isso só funciona em métodos que recebem self como seu argumento e que ele muda o método para um property.

Certos componentes Django também tem sua própria funcionalidade de cache, esses são discutidos abaixo nas seções relacionadas a eles.

Entendendo laziness

Laziness, ou “preguiça” em português, é uma estratégia complementar ao “caching”. Fazer cache evita reprocessamento através do armazenamento dos resultados; laziness posterga o processamento até que ele seja efetivamente solicitado.

Laziness nos permite referenciar coisas antes delas serem instanciadas, ou até mesmo antes de ser possível instanciá-las. Isso pode ser usado de várias formas.

Por exemplo, traduções “lazy” podem ser usadas antes da linguagem alvo se quer ser conhecida, isso porque ela não é usada até que a string a ser traduzida seja efetivamente requerida, como em um template renderizado.

Laziness é também uma forma de salvar recursos através da tentativa de evitar o trabalho desde o início. Isto é, um aspecto do laziness é não fazer nada até que algo realmente tenha que ser feito, porque pode acontecer de o trabalho não ser necessário. Laziness pode, portanto, ter implicações na performance, e quanto mais custoso for o trabalho, maior o potencial de ganho pelo seu uso.

O Python fornece uma série de ferramentas para a avaliação de expressões “lazy”, particularmente através das construções generator e generator expression. É interessante ler mais sobre laziness no Python para descobrir oportunidades de uso desses padrões no seu código.

Laziness no Django

O Django em si é bem “lazy”. Um bom exemplo disso pode ser encontrado na análise de QuerySets. QuerySets são “lazy”. Assim, um QuerySet pode ser criado, passado adiante e combinado com outros QuerySets, sem ocorrerem efetivamente quaisquer viagens ao banco de dados para buscar os itens que ele descreve. O que é passado adiante é o objeto QuerySet, não a coleção de itens que ele - eventualmente - irá solicitar do banco de dados.

Por outro lado, certas operações irão forçar a análise do QuerySet. Evitar a análise prematura de um QuerySet pode salvá-lo de fazer uma desnecessária e custosa viagem até o banco de dados.

O Django também fornece um decorator keep_lazy(). Isso permite que uma função chamada com um argumento lazy se comporte de modo lazy, somente sendo analisada quando for realmente necessário. Assim o argumento lazy - que pode ser um argumento custoso - não será chamado para análise a não ser que isso seja estritamente solicitado.

Banco de Dados

Otimização de banco de dados

Django’s database layer provides various ways to help developers get the best performance from their databases. The database optimization documentation gathers together links to the relevant documentation and adds various tips that outline the steps to take when attempting to optimize your database usage.

Performance HTTP

Middleware

O Django já vem com alguns componentes de middleware úteis que podem ajudar a otimizar a performance do seu site . Eles incluem:

ConditionalGetMiddleware

Adds support for modern browsers to conditionally GET responses based on the ETag and Last-Modified headers. It also calculates and sets an ETag if needed.

GZipMiddleware

Comprime as respostas para todos os browsers modernos, salvando banda e tempo de transferência. Tenha em mente que atualmente o GZipMiddleware é considerado um risco de segurança, e é vulnerável a ataques que anulam a proteção fornecida pelo TLS/SSL. Veja o warning na classe GZipMiddleware para mais informações.

Sessões

Using cached sessions

Usar sessões “cacheadas” pode ser uma forma de melhorar a performance ao eliminar a necessidade de carregar dados de sessão de uma fonte de dados mais lenta como um banco de dados e, ao invés disso, armazenar os dados de sessão mais frequentemente usados em memória.

Arquivos estáticos

Arquivos estáticos, que por definição não são dinâmicos, são um excelente alvo para ganhos de otimização.

ManifestStaticFilesStorage

Tomando vantagem das habilidades de cache dos browsers web, você pode eliminar totalmente o uso de banda para um dado arquivo depois de seu download inicial.

ManifestStaticFilesStorage appends a content-dependent tag to the filenames of static files to make it safe for browsers to cache them long-term without missing future changes - when a file changes, so will the tag, so browsers will reload the asset automatically.

“Minification”

Várias ferramentas e pacotes Django desenvolvidos por terceiros fornecem a habilidade de “minificar” HTML, CSS, e JavaScript. Eles removem espaços em branco desnecessários, quebras de linhas e encurtam nomes de variáveis, e assim reduzem o tamanho dos documentos que o seu site publica.

Performance nos Templates

Tenha em mente que:

  • usar {% block %} é mais rápido do que usar {% include %}
  • Templates muito fragmentados, construídos de várias pequenas peças, podem afetar a performance.

A classe cached template loader

Ativar a classe cached template loader geralmente aumenta a performance drasticamente, já que ela evita compilar cada template novamente sempre que ele tiver que ser renderizado.

Utilizando versões diferentes do mesmo software disponível

Às vezes é bom verificar se versões diferentes e com melhor performance do software que você utiliza estão disponíveis.

Estas técnicas são para os usuários mais avançados que querem expandir as fronteiras de performance de um site Django já bem otimizado.

Entretanto, elas não são soluções mágicas para problemas de performance, e elas provavelmente trarão pouco ou nenhum ganho para sites que já não façam as coisas mais básicas do jeito correto.

Nota

É bom repetir: buscar por alternativas para software que você já usa nunca é a primeira resposta para problemas de performance. Quando você chegar nesse nível de otimização, você precisa de uma solução formal de benchmarking.

Novo é geralmente - mas nem sempre - melhor

It’s fairly rare for a new release of well-maintained software to be less efficient, but the maintainers can’t anticipate every possible use-case - so while being aware that newer versions are likely to perform better, don’t assume that they always will.

Isso é o que tem acontecido no Django em si. Sucessivas novas releases têm oferecido um número de melhorias em todo o sistema, mas você ainda deverá verificar a performance de sua aplicação no mundo real, pois em alguns casos você pode acabar descobrindo que mudanças significam um pior desempenho ao invés do contrário.

Novas versões do Python, e também de suas bibliotecas, também irão com frequência possuir um melhor desempenho - mas meça, ao invés de assumir isso.

Nota

A não ser que você tenha encontrado um problema não usual de performance em uma versão em particular, você geralmente descobrirá melhores funcionalidades, confiabilidade, e segurança em uma nova release e que esses benefícios são muito mais significativos que qualquer performance que você possa ganhar ou perder.

Alternativas a linguagem de templates do Django

Para praticamente todos os casos, a linguagem de templates embutida no Django é perfeitamente adequada. Porém, se o gargalo no seu projeto Django parecer ser o sistema de template e você tiver exaurido outras oportunidades de remediar isso, uma alternativa de terceiros pode ser a resposta.

Jinja2 can offer performance improvements, particularly when it comes to speed.

Sistemas de template alternativos variam no quanto eles usam da linguagem de template do Django.

Nota

Se você tiver problemas de performance nos templates, a primeira coisa a se fazer é entender exatamente o porquê. Utilizar um sistema de template alternativo pode ser mais rápido, mas os mesmos ganhos também podem estar disponíveis sem ter que passar por esse problema - por exemplo, processamento pesado e lógica nos seus templates podem ser feitos de modo mais eficiente em suas views.

Implementações de softwares alternativos

Pode ser útil checar se o software Python que você está usando foi fornecido em uma implementação diferente que pode executar o mesmo código mais rápido.

Entretanto: a maioria dos problemas de performance em sites Django bem escritos não estão no nível de execução Python, mas sim em consultas ineficientes ao banco de dados, caches e templates. Se você estiver se apoiando em código Python mal escrito, seus problemas de performance provavelmente não serão resolvidos ao serem executados mais rápido.

Usar uma implementação alternativa pode introduzir problemas de compatibilidade, implantação, portabilidade, ou manutenção. Nem é preciso dizer que antes de adotar uma implementação atípica você deve se certificar de que ela fornece ganhos de performance suficientes para a sua aplicação que compensem o risco em potencial.

Com essas ressalvas em mente, você deve conhecer:

PyPy

PyPy is an implementation of Python in Python itself (the ‘standard’ Python implementation is in C). PyPy can offer substantial performance gains, typically for heavyweight applications.

A key aim of the PyPy project is compatibility with existing Python APIs and libraries. Django is compatible, but you will need to check the compatibility of other libraries you rely on.

Implementações em C de bibliotecas Python

Algumas bibliotecas Python também são implementadas em C, e podem ser muito mais velozes. Elas visam oferecer as mesmas APIs. Tenha em mente que problemas de compatibilidade e diferenças de comportamento não são desconhecidos (e nem sempre evidentes imediatamente).

Back to Top